vue3中如何使用live2D
作者:Jinuss
本文介绍了如何在Vue3项目中整合Live2D技术,从Live2D的基本介绍到在Vue3中的具体实现方法,Live2D技术允许开发者将二维图像转化为可动画的三维模型,主要应用于游戏、虚拟角色等领域,文章详细说明了在vue3项目中使用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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!