1.准备

1.1下载live2d

live2d官网:https://www.live2d.com 如果下载的慢文章最后有百度云下载链接

1.2安装live2d

一路next,安装好后会有两个文件:
Live2D Cubism Viewer 4.0(这个是查看模型的软件)
Live2D Cubism Editor 4.0(这个是制作模型的软件)
Live2D Cubism Editor 4.0有pro版和free版,用free版就行

1.3 下载sdk





2.制作模型

https://www.bilibili.com/video/av73648216?p=1
这个教程是2.0的,2.0导出的是 .moc 文件,而新版的导出的是 .moc3 文件

注意:导出前要 Ctrl + T 再点击ok一下(生成纹理),不然无法导出
导出后是个文件夹,将文件夹中的 .moc3 文件拖入Live2D Cubism Viewer 4.0软件就能查看效果

3.sdk

官方sdk api:https://docs.live2d.com/cubism-sdk-tutorials/sample-build-web/

3.1运行实例

需要环境:node.js TypeScript Webpack (TypeScript和Webpack安装慢可以使用淘宝镜像)
编辑器打开sdk项目,具体的目录是什么内容可以看项目根目录下的 README.md 文件(windows用户可以使用Typora软件打开.md文件)

  1. 打开控制台跳转到Demo文件夹下输入 npm install 命令(安装了淘宝镜像的可以使用cnpm install命令安装的快一些)
    install命令会根据package.json 文件中的配置下载安装需要的插件
    这里已经安装好了,安装好后在Demo文件夹下回多出一个 node_modules 文件夹
  2. 运行 npm run build命令

    运行完成后会在Demo文件夹下生成一个 dist 文件夹,该文件夹下有一个bundle.js,这个就是集合打包好的js文件,在index.html文件中有引用
  3. 运行 npm run serve命令,启动服务器

    4.在浏览器中输入 http://localhost:5000/Samples/TypeScript/Demo/ 就能看到

    具体的其他命令可以查看跟Demo文件夹同级的 README.md 文件, 在package.json文件中也能看到

3.2源码

想把一些配置放到html中,比如画布(canvas)的大小位置,模型保存的路径等等信息
否则每次需要改变模型的时候都要改代码,重新编译,麻烦
lappdefine.ts //定义基本的参数
lappdelegate.ts //初始化,释放资源,事件绑定
lapplive2dmanager.ts //模型的管理类,进行模型生成和废弃、事件的处理、模型切换。
lappmodel.ts //模型类,定义模型的基本属性
lappal.ts //读取文件,抽象文件数据(算是工具类)
lappsprite.ts //动画精灵类,(学python时知道了精灵类和精灵组)
lapptexturemanager.ts//纹理管理类,进行图像读取和管理的类
lappview.ts //视图类,生成模型的图像被lapplive2dmanager管理
main.ts //主程序启动程序
touchmanager.ts //事件的管理类(比如移动鼠标,点击鼠标,触摸屏触碰等)

这里所有的类都实行单例模式

3.2.2 界面(index.html)

这里用了flask框架(别问为啥简单方便快)

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=1900"><title>TypeScript HTML App</title><link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='live2d/css/live2d.css') }}"/><style>html, body {margin: 0;background-color: #22d7dd;}</style><script type="text/javascript" src="{{ url_for('static', filename='live2d/js/jquery.js') }}"></script><!-- Pollyfill script --><script src="https://unpkg.com/core-js-bundle@3.6.1/minified.js"></script><!-- Live2DCubismCore script --><script src="{{ url_for('static', filename='live2d/js/live2dcubismcore.js') }}"></script><!-- Build script --><script src="{{ url_for('static', filename='live2d/js/bundle.js') }}"></script></head>
<body>1234567890<div class="live2d-main"><div class="live2d-tips"></div><!-- 这里可以定义画布的大小位置 --><canvas id="live2d" width="280" height="250" class="live2d"></canvas><div class="tool"><span class="fui-home"></span><span class="fui-chat"></span><span class="fui-eye"></span><span class="fui-user"></span><span class="fui-photo"></span><span class="fui-info-circle"></span><span class="fui-cross"></span></div></div>
</body>
<script src="{{ url_for('static', filename='live2d/js/message.js') }}"></script>
<script type="text/javascript">var resourcesPath = '/live2d/model/';  // 指定资源文件(模型)保存的路径var backImageName = ''; // 指定背景图片var modelDir = 'Haru,Hiyori,Mark,Natori,Rice,zwt';  // 指定需要加载的模型init();  // 初始化模型,属于message.js文件
</script>
</html>
3.2.2 管理(message.js)
// 初始化
function init(){    var resourcesPaths = `${resourcesPath}`;var backImageNames = `${backImageName}`;var modelDirString = `${modelDir}`;var modelDirs = modelDirString.split(',');initDefine(resourcesPaths, backImageNames, modelDirs);  // lappdefine.ts开放的接口用于初始化常量被编译到bundle.js文件里
}// 监听复制(这里简单添加了一些事件,可以添加更多的事件,比如报时等)
(function() {document.addEventListener('copy',(e)=>{e.preventDefault();e.stopPropagation();showMessage('你都复制了些什么呀,能让我看看吗?', 5000, true); // 显示信息})
}());
// 工具栏的点击事件
$('.tool .fui-home').click(function (){});$('.tool .fui-eye').click(function (){});$('.tool .fui-chat').click(function (){});$('.tool .fui-user').click(function (){});$('.tool .fui-info-circle').click(function (){});$('.tool .fui-cross').click(function (){});$('.tool .fui-photo').click(function (){});function showMessage(text, timeout, flag){if(flag || sessionStorage.getItem('waifu-text') === '' || sessionStorage.getItem('waifu-text') === null){if(Array.isArray(text)) text = text[Math.floor(Math.random() * text.length + 1)-1];//console.log(text);if(flag) sessionStorage.setItem('waifu-text', text);$('.live2d-tips').stop();$('.live2d-tips').html(text).fadeTo(200, 1);if (timeout === undefined) timeout = 5000;hideMessage(timeout);}
}function hideMessage(timeout){$('.live2d-tips').stop().css('opacity',1);if (timeout === undefined) timeout = 5000;window.setTimeout(function() {sessionStorage.removeItem('waifu-text')}, timeout);$('.live2d-tips').delay(timeout).fadeTo(200, 0);
}
3.2.2 基本参数(lappdefine.ts)

由于使用Webpack打包,Typescript文件中的变量和函数被层层括号包围(封装)变成了内部变量和内部函数(具体可以百度Webpack的打包原理),在外部的js文件是调用不到里面的方法的,所以将一些函数或变量挂载到window下,成为全局变量或函数,使外部的js文件也能调用到
在lappdefine.ts文件最后添加

export const win: any = windowwin.initDefine=function(resourcesPath: string, backImageName: string, modelDir: string[]){ResourcesPath = resourcesPath;BackImageName = backImageName;ModelDir = modelDir;ModelDirSize = modelDir.length;
}

这里将initDefine挂载到window下是函数能在外部调用,函数在message.js中调用到
(注意:这里要将ResourcesPathBackImageNameModelDirModelDirSize变量声明成let属性 const 是常量只允许在声明的时候赋值,并且只能赋值一次)

3.2.2 主函数(main.ts)
import { LAppDelegate } from './lappdelegate';// 浏览器装入后的处理(打开页面)
window.onload = (): void => {// create the application instanceif (LAppDelegate.getInstance().initialize() == false) {return;}LAppDelegate.getInstance().run();
};//结束时的处理 (刷新或关闭页面)
window.onbeforeunload = (): void => LAppDelegate.releaseInstance(); //lambda 匿名函数

LAppDelegate.getInstance().initialize() 获得这个类的实例并初始化

3.2.3 LAppDelegate.getInstance().initialize()(lappdelegate.ts)
public initialize(): boolean {// 创建画布// canvas = document.createElement('canvas');// canvas.width = LAppDefine.RenderTargetWidth;// canvas.height = LAppDefine.RenderTargetHeight;// 原来是用js动态在网页上创建画布,画布的长宽在lappdefine.ts指定,现在直接在html中已经有了画布直接拿过来使用就行canvas = <HTMLCanvasElement>document.getElementById("live2d"); // index.html中的id为live2d的画布canvas.width = canvas.width;canvas.height = canvas.height;canvas.toDataURL("image/png");// 这个是index.html工具栏中的眼睛图标,点击眼睛图标就切换下一个模型// 正规来说应该留个切换模型的口子,在message.js中调用,因为懒就直接在这里写了fui_eye = <HTMLSpanElement>document.getElementsByClassName("fui-eye")[0];// 初始化gl上下文 (代码段结束后有解释)// @ts-ignoregl = canvas.getContext('webgl',{alpha: true }) || canvas.getContext('experimental-webgl');if (!gl) {alert('Cannot initialize WebGL. This browser does not support.\n不能初始化WebGL,该浏览器不支持WebGL,请切换浏览器重试');gl = null;document.body.innerHTML ='该浏览器不支持 <code>&lt;canvas&gt;</code> 标签元素,请切换浏览器重试 .';// gl初期化失敗return false;}// 向DOM添加画布// document.body.appendChild(canvas);  if (!frameBuffer) {frameBuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);}// 透明设置gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);const supportTouch: boolean = 'ontouchend' in canvas;  //是否支持触碰(触摸屏)if (supportTouch) {   // 没有触屏电脑(两种事件都要注册)// 注册触摸相关的回掉函数  (触摸屏)canvas.ontouchstart = onTouchBegan;canvas.ontouchmove = onTouchMoved;canvas.ontouchend = onTouchEnded;canvas.ontouchcancel = onTouchCancel;} else {// 注册鼠标相关的回呼函数canvas.onmousedown = onClickBegan;// canvas.onmousemove = onMouseMoved;   //原来是在画布上注册鼠标移动事件,鼠标移出画布就监听不到window.onmousemove = onMouseMoved;  //对整个window窗口监听,是角色跟随鼠标,需要对鼠标坐标获取做调整canvas.onmouseup = onClickEnded;fui_eye.onmousedown = (): void => {    // 工具栏眼睛图标点击事件const live2DManager: LAppLive2DManager = LAppLive2DManager.getInstance();live2DManager.nextScene();};}// AppView的初始化this._view.initialize();// Cubism SDK的初始化this.initializeCubism();return true;}

contextType参数有以下四种:
注:早期WebGL的context,还不能通过正式的名称webgl来获取,必须使用experimental-webgl来获取context对象。

2d”,创建一个CanvasRenderingContext2D对象作为2D渲染的上下文。
webgl”(或“experimental-webgl”),创建一个WebGLRenderingContext对象作为3D渲染的上下文,只在实现了WebGL 2的浏览器上可用,实验性特性。
webgl2”,创建一个WebGL2RenderingContext对象作为3D渲染的上下文,只在实现了WebGL 3的浏览器上可用。
bitmaprenderer”,创建一个ImageBitmapRenderingContext,用于将位图渲染到canvas上下文上,实验性特性。

原文链接:https://blog.csdn.net/acoolgiser/article/details/85800799

onMouseMoved(lappdelegate.ts)
// 鼠标移动后的回掉
function onMouseMoved(e: MouseEvent): void {// if (!LAppDelegate.getInstance()._captured) {  // 判断是否单击,原来是要按住鼠标左键图像才会跟着鼠标动//   return;// }if (!LAppDelegate.getInstance()._view) {   //获得lappview.ts的实例对象LAppPal.printMessage('view notfound');return;}// e.clientX和e.clientY获取的坐标点都是以左上角为原点const rect = (e.target as Element).getBoundingClientRect();// const posX: number = e.clientX - rect.left;// const posY: number = e.clientY - rect.top;let posX: number = e.clientX;let posY: number = e.clientY - window.innerHeight + canvas.height;// 图像在网页的坐下角,简单处理坐标将超过画布边界坐标就等与边界坐标posX = (posX > canvas.width) ? canvas.width : posX;posY = (posY < 0) ? 0 : posY;LAppDelegate.getInstance()._view.onTouchesMoved(posX, posY);// 这个就不做解释,就是转换坐标,调用LAppLive2DManager类重新绘制图像
}

假设屏幕是一个九宫格(万能神奇的九宫格哈哈)

回到**initialize()**函数,在initialize()后有两个初始化的函数
this._view.initialize() (lappview.ts)主要就是指定一些图像的参数,例如画面的范围相对,设置当前矩阵的放大率等
this.initializeCubism()(lappdelegate.ts)

3.2.4 this.initializeCubism() (lappdelegate.ts)
public initializeCubism(): void {// setup cubism 设置cubismthis._cubismOption.logFunction = LAppPal.printMessage;  //初始化控制台打印信息工具,就是console.logthis._cubismOption.loggingLevel = LAppDefine.CubismLoggingLevel;  //指定打印日志的等级Csm_CubismFramework.startUp(this._cubismOption);// initialize cubism 初始化设置cubismCsm_CubismFramework.initialize();// load model 加载模型LAppLive2DManager.getInstance();// 更新时间LAppPal.updateTime();this._view.initializeSprite();}

Csm_CubismFramework.initialize()底层的初始化设置
LAppLive2DManager.getInstance()模型管理类的初始化,单例模型没什么好说的,注意这个类在构造方法中会加载模型下段代码所示:

// (lapplive2dmanager.ts)
public changeScene(index: number): void {this._sceneIndex = index;if (LAppDefine.DebugLogEnable) {   //要是调试的情况下打印信息LAppPal.printMessage(`[APP]model index: ${this._sceneIndex}`);}// 从ModelDir[]中保存的目录名称// 要使目录名和model 3.json的名字一致。const model: string = LAppDefine.ModelDir[index];const modelPath: string = LAppDefine.ResourcesPath + model + '/';let modelJsonName: string = LAppDefine.ModelDir[index];modelJsonName += '.model3.json';    //拼接生成模型路径this.releaseAllModel();    //清除原来显示的模型this._models.pushBack(new LAppModel());   // 推入管理栈堆this._models.at(0).loadAssets(modelPath, modelJsonName);  //加载模型,lappmodel.ts异步请求服务器模型资源}//  构造器constructor() {this._viewMatrix = new Csm_CubismMatrix44();this._models = new Csm_csmVector<LAppModel>();this._sceneIndex = 0;this.changeScene(this._sceneIndex);      //第一次加载模型}

这里在往下深入就是 lappmodel.ts 加载定义相关的模型信息例如模型的大小等
loadAssets(modelPath, modelJsonName)异步加载模型的json文件到缓存中===>
CubismModelSettingJson(buffer, size)模型json文件的缓存,和缓存大小===>
this.setupModel(setting: CubismModelSettingJson); 根据模型json文件中的信息异步请求去加载模型及相关的文件(例如动作文件、物理文件等)===>
loadModel(buffer: ArrayBuffer) 模型文件缓存,去加载生成模型===>
this._modelMatrix = new CubismModelMatrix(this._model.getCanvasWidth(), this._model.getCanvasHeight());根据画布的大小去生成模型===>
this.setHeight(1.0);根据画布的高度去生成一个正方形模型坐标系(4*4)调整里面的参数可以调整模型区域的大小(玄学的数字为什么没看懂,有待研究)

3.2.5 this._view.initializeSprite() (lappview.ts)

回到 LAppDelegate.initialize()(lappdelegate.ts),在initialize()的最后会调用 this._view.initializeSprite()方法

  // 进行图像的初始化,一些不重要的元素初始化。这里有一个齿轮设置的图像,里面的内容替换成了眼睛的图标,没用所以注释掉,还加了一个背景图片加载的判断,没有背景图片就不加载public initializeSprite(): void {const width: number = canvas.width;const height: number = canvas.height;const textureManager = LAppDelegate.getInstance().getTextureManager();   // 从LAppDelegate类中得到纹理管理器const resourcesPath = LAppDefine.ResourcesPath;let imageName = '';// 背景图像初始化imageName = LAppDefine.BackImageName;if(imageName != "" && imageName != null){ //如果指定了背景图片,就加载// 由于异步,创建回调函数const initBackGroundTexture = (textureInfo: TextureInfo): void => {const x: number = width * 0.5;  //背景图片出现宽度的位置const y: number = height * 0.5; //背景图片出现高度的位置 const fwidth = textureInfo.width * 2.0;   //背景图片的宽度const fheight = height * 0.95;            //背景图片的高度this._back = new LAppSprite(x, y, fwidth, fheight, textureInfo.id);  //绘制背景图片};textureManager.createTextureFromPngFile(  //回掉函数resourcesPath + imageName,false,initBackGroundTexture);}// 齿轮图像初始化 (原来是右上角有一个齿轮的图片,点击齿轮图片切换模型)// imageName = LAppDefine.GearImageName;// // 齿轮初始化后的回掉函数// const initGearTexture = (textureInfo: TextureInfo): void => {//   const x = width - textureInfo.width * 0.5;   //出现在右上角 //   const y = height - textureInfo.height * 0.5;//   const fwidth = textureInfo.width;//   const fheight = textureInfo.height;//   this._gear = new LAppSprite(x, y, fwidth, fheight, textureInfo.id);// };// textureManager.createTextureFromPngFile(//   resourcesPath + imageName,//   false,//   initGearTexture// );// 创建阴影if (this._programId == null) {this._programId = LAppDelegate.getInstance().createShader();}}

到这里初始化的工作基本完成了

3.2.6 LAppDelegate.getInstance().run() (lappdelegate.ts)

回到main.ts文件接下来就是执行LAppDelegate.getInstance().run()方法,没啥好说的,就是不断循环刷新画布,达到动画的效果

  // 执行处理public run(): void {// 主循环const loop = (): void => {// 确认有无实例if (s_instance == null) {return;}// 更新时间LAppPal.updateTime();// 画面的初始化gl.clearColor(0.0, 0.0, 0.0, 1.0);// 启动深度测试gl.enable(gl.DEPTH_TEST);// 附近的物体将远处的物体遮盖起来gl.depthFunc(gl.LEQUAL);// 清除彩色缓冲区和深度缓冲区  (加上这一句会导致有些浏览器背景变成黑色,而不是透明)// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);gl.clearDepth(1.0);// 透明设置gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);// 绘图更新this._view.render();// 循环递归调用requestAnimationFrame(loop);};loop();}

最后打包bundle.js,赋值bundle.js文件和Core核心文件live2dcubismcore.js就可以移植到任何项目了

4成果


最后的模型是我做的哈哈,比较简单只会摇头眨眼,图片来自于网络,仅供于学习
链接: https://pan.baidu.com/s/1SV-R3OCGlQ37BrPqKzo7lg 提取码: xg29

笔记:live2d4.0 sdk 博客园网页动画相关推荐

  1. PageRank 计算博客园用户排名

    PageRank 通过网页与网页之间的链接关系计算各网页权重,一般权重高的网页特点是:链接向它的网页数量多.链向它的网页其权重也较高.PageRank 就是通过这样的连接关系,一轮轮迭代计算后得出各网 ...

  2. 用WORD2010写博客,并发送至博客园

    博客园网页版写博客总是那么的不爽,特别是右侧那两条滚动条,水瓶座的绝对受不了. 注:本文是根据"六仙庵"的博客,加以整理. 一.配置账号 1.1点击"发布为博客文章&qu ...

  3. 互评Alpha版本—博客园安卓APP

    互评Alpha版本-博客园安卓APP 测评人:徐劭斌 基于NABCD评论作品 1.根据NABCD评论作品的选题:   N(Need,需求):博客园的网页版深受软件开发人员的喜爱.博客园是一个非常好的学 ...

  4. 互评Beta版本--博客园安卓APP

    测评人:徐劭斌 博客园安卓APP软件说明书地址:http://www.cnblogs.com/wwd1993/p/7880856.html 博客园安卓APP软件下载git地址:https://git. ...

  5. 【教程】一步一步教你如何自定义设置——博客园canvas/JS交互动画背景

    演示步骤的原文链接:(看完就可以掌握如何设置博客园canvas动画背景) 博客园如何自定义设置canvas/JS动画背景 --2018-10-27 本博客的弹弹弹小球-canvas动画原文链接:(看完 ...

  6. 笔记:编程的一些建议 - 时间伙伴 - 博客园

    笔记:编程的一些建议 0.今天偶然和同学聊到Eric Steven Raymond的<大教堂与集市>,想起可能会有些同学没看过,给个链接吧.http://www.yeeyan.com/ar ...

  7. bootstrap实现网页手风琴--博客园老牛大讲堂

    网页常用有网页的左右导航,其中用到了bootstap框架. 例子一. <!DOCTYPE html> <html><head><meta charset=&q ...

  8. Spring Security笔记:使用数据库进行用户认证(form login using database) - 菩提树下的杨过 - 博客园...

    Spring Security笔记:使用数据库进行用户认证(form login using database) - 菩提树下的杨过 - 博客园 在前一节,学习了如何自定义登录页,但是用户名.密码仍然 ...

  9. redis源码笔记 - 刘浩de技术博客 - 博客园

    redis源码笔记 - 刘浩de技术博客 - 博客园 redis源码笔记 - 刘浩de技术博客 - 博客园 redis源码笔记 记录发现的一个hiredis的bug 摘要: hiredis是redis ...

  10. 安卓开发笔记——打造属于自己的博客园APP(一)

    最近事情比较多,博客更新又落下了,平时有个习惯,喜欢睡前看看博客园里博友的文章,但一直感觉APP市场上下载下来的博客园客户端用起来并不是很舒服,近来发现博客园也有对外开放的数据接口,所以打算自己写个博 ...

最新文章

  1. Kafka 安装和搭建 (一)
  2. .zip.001 -- .zip.003解压缩
  3. 软件测试技术lab1 2017.3.13
  4. 大一python基础编程试卷_2020大学慕课Python编程基础试题及答案
  5. 第22篇 js中的this指针的用法
  6. mongodb 索引1
  7. MATLAB鲁棒控制器实现
  8. 人工智能培训:是学不好,还是教不好?3岁半的 BitTiger关闭
  9. ps图层高级扩展知识
  10. 调用百度地图API进行当前位置定位失败解决方法
  11. phpmail通过qq发邮箱失败_请问phpmailer发送Gmail总是失败是什么原因,qq邮件可以发送出去...
  12. 建立arm linux运行环境,构建 arm-linux 仿真运行环境 (skyeye + arm-linux + NFS)
  13. linux中etc目录的英文全称
  14. [lua] 用lua实现扑克游戏发牌的逻辑代码
  15. 刷机大师乐蛙定制版 v3.4.0 官方版
  16. java 三点_[Java教程]三点运算符使用方法
  17. html5 手机相册裁剪,H5的手机图片裁剪特效
  18. 前端人工智能?TensorFlow.js 学会游戏通关
  19. 使用成功路径实现数据库状态回传
  20. iStylePDF把多个PDF合并成一个PDF文件

热门文章

  1. 神武手游哪个服务器人最多,神武4端游什么老区人最多
  2. 二叉树模型matlab实现,利用Matlab实现二叉树的树形显示
  3. 实现简单的滑块验证代码案例
  4. 已知特征多项式求所有对应的二阶矩阵
  5. TCP/IP常见英文缩写
  6. 十三种Java开发工具
  7. PPI | protein-protein interaction | 蛋白互作分析 | gene interaction | 基因互作
  8. java中CheckException和UnCheckException的区别
  9. C++实现经典同步问题(生产者消费者、读者写者、哲学家进餐、吸烟者问题)
  10. win7 局域网访问网站