当前位置: 代码网 > it编程>前端脚本>Vue.js > vue3中如何使用live2D

vue3中如何使用live2D

2024年10月27日 Vue.js 我要评论
概述本文将介绍如何在vue3项目中使用live2d。live2d 介绍live2d 是什么live2d 是一种用于将二维图像转化为可动画三维模型的技术,主要应用于游戏、虚拟角色和互动应用中。它允许开发

概述

本文将介绍如何在vue3项目中使用live2d

live2d 介绍

live2d 是什么

live2d 是一种用于将二维图像转化为可动画三维模型的技术,主要应用于游戏、虚拟角色和互动应用中。它允许开发者通过对静态图像进行分层和建模,使角色在不同角度下能够进行流畅的动作和表情变化。

live2d 主要特点

  • 动态表现:live2d 可以让角色在不改变原始图像的情况下,实现多种动作和表情,例如眨眼、微笑、转头等。
  • 用户交互:它可以与用户的输入进行互动,比如鼠标移动或触摸屏幕时,角色会作出相应的反馈。
  • 应用广泛:被广泛应用于手机游戏、动画、虚拟直播、社交软件等领域。
  • 易于使用:通过 live2d 提供的工具(如 live2d cubism),艺术家可以方便地创建和编辑模型,无需深入的编程知识。

live2d 效果

vue3 中使用 live2d

如果前端项目是用vite搭建,则需要在index.html中引入live2d.min.js库,因为在 vue 组件中引入会报错。

封装 live 组件

template

template部分如下:

<template>
  <div
    :class="{
      'vue-live2d': true,
      'vue-live2d-on-left': true,
      // 'vue-live2d-on-right': direction === 'right',
    }"
    :style="{
      width: `300px`,
      height: `300px`,
      position: 'absolute',
      left: '10px',
      bottom: '20px',
    }"
    @mouseover="openlive2dtool"
    @mouseout="closelive2dtool"
  >
    <div
      v-show="true"
      v-html="data.tiptext"
      :class="{
        'vue-live2d-tip': true,
        'vue-live2d-tip-on-top': true,
        // 'vue-live2d-tip-on-bottom': tipposition === 'bottom',
      }"
    ></div>
    <canvas
      :id="customid"
      v-show="mainshow"
      :class="{
        'vue-live2d-main': true,
        'vue-live2d-main-on-left': true,
        // 'vue-live2d-main-on-right': direction === 'right',
      }"
      width="300"
      height="300"
    >
    </canvas>
    <div v-show="toolshow" class="vue-live2d-tool">
      <span
        v-for="(tool, index) in tools"
        :key="index"
        :class="tool.classname"
        v-html="tool.svg"
        @click="tool.click"
      />
    </div>
    <div
      v-show="toggleshow"
      @click="openlive2dmain"
      :class="{
        'vue-live2d-toggle': true,
        'vue-live2d-toggle-on-left': true,
        // 'vue-live2d-toggle-on-right': direction === 'right',
      }"
    >
      <span>kanban girl</span>
    </div>
  </div>
</template>

template部分主要是定义 ui 部分,以及绑定界面点击和hove的事件,live2d的模型是在 canvas 中绘制的。

核心逻辑
核心逻辑主要就是加载live2d的模型以及定义事件,其实现如下:

<script setup>
import { onmounted, nexttick, ref, computed } from "vue";
import tips from "./options/tips";
// const model = ["potion-maker/pio", "school-2017-costume-yellow"];
const model = ["shizukutalk/shizuku-48", "default"];
const apipath = "https://evgo2017.com/api/live2d-static-api/indexes";
let [modelpath, modeltexturesid] = model;
const customid = "vue-live2d-main";
let messagetimer = null;
const data = ref({
  containerdisplay: {
    tip: false,
    main: true,
    tool: false,
    toggle: false,
  },
  tiptext: "vue-live2d kanban girl",
  modeltexturesid: modeltexturesid,
});
const changelive2dsize = () => {
  document.queryselector(
    `#${customid}`
  ).outerhtml = `<canvas id=${customid} width="300" height="300" class="vue-live2d-main"></canvas>`;
  loadmodel();
};
const loadmodel = () => {
  window.loadlive2d(
    customid,
    `${apipath}/${modelpath}/${data.value.modeltexturesid}.json`
  );
  console.log(
    `live2d 模型 ${modelpath},服装 ${data.value.modeltexturesid} 加载完成`
  );
};
const loadrandmodel = () => {
  http({
    url: `${apipath}/models.json`,
    success: (data) => {
      const models = data.filter(({ modelpath: i }) => i !== modelpath);
      const { modelpath: j, modelintroduce } =
        models[math.floor(math.random() * models.length)];
      modelpath = j;
      showmessage(`${modelintroduce}`, 4000);
      loadrandtextures(true);
    },
  });
};
const loadrandtextures = (isafterrandmodel = false) => {
  http({
    url: `${apipath}/${modelpath}/textures.json`,
    success: (resp) => {
      const modeltexturesids = resp.filter(
        (modeltexturesid) => modeltexturesid !== data.value.modeltexturesid
      );
      data.value.modeltexturesid =
        modeltexturesids[math.floor(math.random() * modeltexturesids.length)];
      loadmodel();
      if (!isafterrandmodel) {
        showmessage("我的新衣服好看嘛?", 4000);
      }
    },
  });
};
const showmessage = (msg = "", timeout = 6000) => {
  if (messagetimer) {
    cleartimeout(messagetimer);
    messagetimer = null;
  } else {
    data.value.containerdisplay.tip = true;
  }
  data.value.tiptext = msg;
  messagetimer = settimeout(() => {
    data.value.containerdisplay.tip = false;
    messagetimer = null;
  }, timeout);
};
const takephoto = () => {
  showmessage("照好了嘛,留个纪念吖~");
  window.live2d.capturename = "photo.png";
  window.live2d.captureframe = true;
};
const showhitokoto = () => {
  http({
    url: "https://v1.hitokoto.cn",
    success: ({ hitokoto, id, creator, from }) => {
      showmessage(
        `${hitokoto} <br> - by <a href="https://hitokoto.cn?id=${id}" rel="external nofollow" >${creator}</a> from 《${from} 》`
      );
    },
  });
};
const closelive2dmain = () => {
  data.value.containerdisplay.main = false;
};
const openlive2dmain = () => {
  data.value.containerdisplay.main = true;
};
const closelive2dtool = () => {
  data.value.containerdisplay.tool = false;
};
const openlive2dtool = () => {
  data.value.containerdisplay.tool = true;
};
const loadevent = () => {
  for (const event in tips) {
    for (const { selector, texts } of tips[event]) {
      const dom =
        selector === "document" ? document : document.queryselector(selector);
      if (dom == null) {
        continue;
      }
      dom.addeventlistener(event, () => {
        const msg = texts[math.floor(math.random() * texts.length)];
        showmessage(msg, 2000);
      });
    }
  }
};
const http = ({ url, success }) => {
  const xhr = new xmlhttprequest();
  xhr.onreadystatechange = function () {
    if (xhr.readystate === 4) {
      if (xhr.status >= 200 || xhr.status < 300 || xhr.status === 304) {
        success && xhr.response && success(json.parse(xhr.response));
      } else {
        console.error(xhr);
      }
    }
  };
  xhr.open("get", url);
  xhr.send(null);
};
nexttick(() => {
  loadevent();
});
onmounted(() => {
  loadmodel();
});
defineprops({
  tips: {
    default: () => tips,
    type: object,
  },
  width: {
    default: 0,
    type: number,
  },
  height: {
    default: 0,
    type: number,
  },
  size: {
    default: 255,
    type: number,
  },
});
const tipshow = computed(() => {
  return mainshow && data.value.containerdisplay.tip;
});
const mainshow = computed(() => {
  return data.value.containerdisplay.main;
});
const toolshow = computed(() => {
  return mainshow && data.value.containerdisplay.tool;
});
const toggleshow = computed(() => {
  return !mainshow;
});
const tools = ref([
  {
    classname: "custom-fa-comment",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m256 32c114.6 32 0 125.1 0 240c0 49.6 21.4 95 57 130.7c44.5 421.1 2.7 466 2.2 466.5c-2.2 2.3-2.8 5.7-1.5 8.7s4.8 480 8 480c66.3 0 116-31.8 140.6-51.4 32.7 12.3 69 19.4 107.4 19.4 141.4 0 256-93.1 256-208s397.4 32 256 32z"/></svg>',
    click: showhitokoto,
  },
  {
    classname: "custom-fa-user-circle",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 496 512" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m248 8c111 8 0 119 0 256s111 248 248 248 248-111 248-248s385 8 248 8zm0 96c48.6 0 88 39.4 88 88s-39.4 88-88 88-88-39.4-88-88 39.4-88 88-88zm0 344c-58.7 0-111.3-26.6-146.5-68.2 18.8-35.4 55.6-59.8 98.5-59.8 2.4 0 4.8.4 7.1 1.1 13 4.2 26.6 6.9 40.9 6.9 14.3 0 28-2.7 40.9-6.9 2.3-.7 4.7-1.1 7.1-1.1 42.9 0 79.7 24.4 98.5 59.8c359.3 421.4 306.7 448 248 448z"/></svg>',
    click: loadrandmodel,
  },
  {
    classname: "custom-fa-street-view",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m367.9 329.76c-4.62 5.3-9.78 10.1-15.9 13.65v22.94c66.52 9.34 112 28.05 112 49.65 0 30.93-93.12 56-208 56s48 446.93 48 416c0-21.6 45.48-40.3 112-49.65v-22.94c-6.12-3.55-11.28-8.35-15.9-13.65c58.87 345.34 0 378.05 0 416c0 53.02 114.62 96 256 96s256-42.98 256-96c0-37.95-58.87-70.66-144.1-86.24zm256 128c35.35 0 64-28.65 64-64s291.35 0 256 0s-64 28.65-64 64 28.65 64 64 64zm-64 192v96c0 17.67 14.33 32 32 32h64c17.67 0 32-14.33 32-32v-96c17.67 0 32-14.33 32-32v-96c0-26.51-21.49-48-48-48h-11.8c-11.07 5.03-23.26 8-36.2 8s-25.13-2.97-36.2-8h208c-26.51 0-48 21.49-48 48v96c0 17.67 14.33 32 32 32z"/></svg>',
    click: loadrandtextures,
  },
  {
    classname: "custom-fa-camera-retro",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m48 32c21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48v80c0-26.5-21.5-48-48-48h48zm0 32h106c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6h38c-3.3 0-6-2.7-6-6v80c0-8.8 7.2-16 16-16zm426 96h38c-3.3 0-6-2.7-6-6v-36c0-3.3 2.7-6 6-6h138l30.2-45.3c1.1-1.7 3-2.7 5-2.7h464c8.8 0 16 7.2 16 16v74c0 3.3-2.7 6-6 6zm256 424c-66.2 0-120-53.8-120-120s53.8-120 120-120 120 53.8 120 120-53.8 120-120 120zm0-208c-48.5 0-88 39.5-88 88s39.5 88 88 88 88-39.5 88-88-39.5-88-88-88zm-48 104c-8.8 0-16-7.2-16-16 0-35.3 28.7-64 64-64 8.8 0 16 7.2 16 16s-7.2 16-16 16c-17.6 0-32 14.4-32 32 0 8.8-7.2 16-16 16z"/></svg>',
    click: takephoto,
  },
  {
    classname: "custom-fa-info-circle",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m256 8c119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248c504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"/></svg>',
    click: () => {},
  },
  {
    classname: "custom-fa-times",
    svg: '<svg xmlns="http://www.w3.org/2000/svg" viewbox="-40 -40 432 592" fill="currentcolor" height="20px" width="20px"><!-- font awesome free 5.15.4 by @fontawesome - https://fontawesome.com license - https://fontawesome.com/license/free (icons: cc by 4.0, fonts: sil ofl 1.1, code: mit license) --><path d="m242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0l176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0l9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48l109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0l176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48l242.72 256z"/></svg>',
    click: closelive2dmain,
  },
]);
</script>

demo地址

demo地址和具体示例可参考:https://github.com/jinuss/maps

到此这篇关于vue3中使用live2d的文章就介绍到这了,更多相关vue3 使用live2d内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com