如何在前端网页实现live2d的动态效果

React如何在前端网页实现live2d的动态效果

业务需求:

因为公司需要做机器人相关的业务,主要是聊天形式的内容,所以需要一个虚拟的卡通形象。而且为了更直观的展示用户和机器人对话的状态,该live2d动画的嘴型需要根据播放的内容来动。

相关技术原理:

1. 网页上使用live2d的相关技术原理

Live2D是一种先进的技术,它允许艺术家和开发者创建具有高度表现力的二维角色动画。这些角色可以根据用户的交互进行动态响应,从而提供一种非常生动和吸引人的用户体验。Live2D广泛应用于游戏、虚拟偶像、广告和移动应用程序中。

Live2D由日本Cybernoids公司开发,旨在创造出能够进行平滑动作和表情变化的二维角色,而不需要传统的帧动画。这是通过对二维图像进行切割和变形来实现的,使得角色能够以看似三维的方式移动和表达情感。

  1. WebGL和Canvas:Live2D在网页上的实现依赖于WebGL(一个JavaScript API,用于在任何兼容的网页浏览器中渲染高性能的交互式3D和2D图形,而不需要使用插件)和Canvas元素(提供了一个用于绘制图形的2D环境)。通过这些技术,开发者可以在用户的浏览器中直接渲染Live2D模型。
  2. Live2D Cubism SDK:为了在网页上使用Live2D,开发者需要利用Live2D Cubism SDK for Web。这个SDK提供了一系列的API,允许开发者加载、渲染和控制Live2D模型。它处理模型的动画、变形和用户交互。
  3. 模型和动画数据:Live2D模型通常存储在特定格式(如.moc或.moc3)的文件中,这些文件包含了角色的图形和骨架信息。动画数据(如表情和动作)通常以JSON或其他格式存储,可以通过SDK的API来播放。
  4. 交互性:通过JavaScript和SDK的API,开发者可以实现模型对鼠标移动、点击等用户交互的响应。例如,让角色的眼睛跟随鼠标移动,或者在点击时播放特定的动画。
  5. 性能优化:为了确保流畅的用户体验,特别是在性能有限的设备上,Live2D在网页上的实现需要进行性能优化。这包括合理控制动画的帧率、减少不必要的资源加载和使用WebGL的高效特性。

2. 文字转语音 TTS

本项目使用的是讯飞的TTS在线文字转语音功能。使用Websocket实现客户端和讯飞服务器之间的通信。总体逻辑就是客户端把需要播报的文字发送给讯飞的服务端,服务端经过处理后,把文字转成了二进制的音频流返回给客户端,然后客户端使用适当的方法播放解析后的音频数据,完成从文字到语音的整个过程。

不过这项功能不是本次的内容的重点,另外详解。

3. 语音转文字 IAT

不是本次的内容的重点,另外详解。

项目实现

1. 引入live2d相关库

Cubism Web是一个基于Web技术的软件开发工具包,用于创建Live2D。Cubism Web提供了一组工具和库,使开发人员可以轻松地将Live2D模型集成到Web应用程序中。它支持多种平台和框架,包括HTML5、JavaScript和CSS,因此可以在各种设备和浏览器上运行。

官网如下:

https://www.live2d.com/zh-CHS/sdk/download/web/

下载Cubism SDK文件:

img

解压后的目录如下,其中Core核心库和Framework框架库是我们所需要的。

img

  1. Core

将上述展示的Core文件夹复制到项目的public文件下。

img

同时,在public文件夹下面的index.html中加上如下代码:

<script src = "%PUBLIC_URL%/Core/live2dcubismcore.js"></script>

img

  1. framework

同理,将上述展示的Framework移动到项目的src文件夹下。注意不是pubilc文件夹下了。直接放到src文件夹即可,或者其他目录也行,引用的时候知道地址就行。

img

  1. Resources

Resources文件夹下放的是live2d的模型,具体模型的内容可以去官网下载简单的Model,也可以根据需要找设计师去设计你需要的Modal。笔者这边只是展示了项目里面一个Modal里面的内容。

img

官方提供的Modal地址:

https://www.live2d.com/zh-CHS/sdk/download/web/

  1. Render

以上文件准备好后,可以做页面渲染相关的工作了。根据官方给的Demo,如下图所示:

img

根据Demo中main.ts中的样例可以推断出,这段代码用于实现Live2D模型的初始化、渲染和响应浏览器事件。

  1. 导入必要的模块:首先,代码通过import语句导入了LAppDelegate、LAppDefine和LAppGlManager等模块,这些模块包含了Live2D模型渲染所需的各种功能和配置。
  2. 浏览器加载完成后的处理:通过监听load事件,当浏览器完成加载后,执行初始化WebGL和创建应用实例的操作。这里涉及到两个关键的步骤:
  • 检查LAppGlManager的实例是否存在,以及调用LAppDelegate的initialize方法进行初始化。这些操作确保了WebGL环境的正确设置和应用的初始化。
  • 调用LAppDelegate.getInstance().run()启动Live2D模型的渲染循环。
  1. 浏览器关闭前的处理:监听beforeunload事件,当浏览器准备关闭前,释放LAppDelegate的实例,以确保资源被正确清理。
  2. 浏览器窗口尺寸变化的处理:监听resize事件,当浏览器窗口尺寸发生变化时,根据LAppDefine.CanvasSize的配置决定是否调用LAppDelegate.getInstance().onResize()方法来调整Live2D模型的尺寸。这保证了模型在不同尺寸的屏幕上都能正确显示。
/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

import { LAppDelegate } from './lappdelegate';
import * as LAppDefine from './lappdefine';
import { LAppGlManager } from './lappglmanager';

/**
 * ブラウザロード後の処理
 */
window.addEventListener(
  'load',
  (): void => {
    // Initialize WebGL and create the application instance
    if (
      !LAppGlManager.getInstance() ||
      !LAppDelegate.getInstance().initialize()
    ) {
      return;
    }

    LAppDelegate.getInstance().run();
  },
  { passive: true }
);

/**
 * 終了時の処理
 */
window.addEventListener(
  'beforeunload',
  (): void => LAppDelegate.releaseInstance(),
  { passive: true }
);

/**
 * Process when changing screen size.
 */
window.addEventListener(
  'resize',
  () => {
    if (LAppDefine.CanvasSize === 'auto') {
      LAppDelegate.getInstance().onResize();
    }
  },
  { passive: true }
);

而我们需要做的,则是把Demo中除了main.ts的其他文件Copy到项目中在自有的项目中,Render的相关目录如图所示:

img

2. 用React转写main.ts

然后main.ts实现的逻辑,用React的方式进行重构,在index.js中。本文只展示核定代码,因为实际情况会根据需求,增加很多其他的业务实现代码,比如笔者最初的需求是增加live2d的收听、播放等动画效果,所以需要额外传入sourceBuffer等数据。

  1. 导入依赖:首先,代码导入了React相关的hooks(如useEffect, useRef, useMemo等),以及Live2D相关的配置和管理模块(LAppDelegate, LAppLive2DManager, LAppDefine等)。
  2. 组件结构:ReactLive2d组件通过return语句返回一个包含元素的结构,这个元素被指定了id=“live2d”,以及动态计算的width和height属性。这个就是用来渲染Live2D动画的画布。
  3. 初始化和配置
  • 在组件挂载时(useEffect),根据传入的props(如live2dModelId)动态设置Live2D模型的目录。
  • 如果满足条件(非移动设备或允许在移动设备上显示),则初始化Live2D应用(LAppDelegate.getInstance().initialize())并运行(LAppDelegate.getInstance().run())。
  1. 音频处理:组件还处理了音频输入(通过audioContext和source),并将音频数据通过rmsRef(一个自定义的音频处理引用)连接到Live2D动画,可能用于根据音频数据动态调整Live2D动画的表情或动作。
  2. 清理资源:在组件卸载时,清理资源,如销毁rmsRef引用和释放Live2D应用实例。
import React, { useEffect, useRef, useMemo, useCallback } from 'react';
import AudioRMS from '@/utils/AudioProcess';
import { Button } from 'antd-mobile';

import { LAppDelegate } from './live2dConfig/lappdelegate';
import { LAppLive2DManager } from './live2dConfig/lapplive2dmanager';
import * as LAppDefine from './live2dConfig/lappdefine';
import './index.css';


function ReactLive2d(props, ref) {
  const { width, height, audioContext, source, live2dModelId, isTTSPlaying } = props;
  const rmsRef = useRef(null);

  // setModelDir()用于根据外部传的Model Id动态的引入模型,如果没有这个需求可以就写死Model的逻辑即可
  useEffect(() => {
    // 根据外部传入的modelId,动态设置model
    LAppDefine.lappdefineSet.setModelDir([live2dModelId]);
    if (!navigator.userAgent.match(/mobile/i)) {
      if (LAppDelegate.getInstance().initialize()) {
        LAppDelegate.getInstance().run();
      }
    }
    return () => {
      rmsRef.current && rmsRef.current.destroy && rmsRef.current.destroy();
      LAppDelegate.releaseInstance();
    }
  }, []);

  const configRmsWithAudio = () => {
    if (source) {
      source.connect(rmsRef.current.input)
    }
  }
  useEffect(() => {
    configRmsWithAudio();
  }, [audioContext, source])

  const onData = useCallback((data) => {
    LAppLive2DManager.getInstance().setRmsToValue(data[0])
  }, []);

  useEffect(() => {
    if (audioContext) {
      if (!rmsRef.current) {
        rmsRef.current = AudioRMS(audioContext, 'sqr')
      }
      if (isTTSPlaying) {
        rmsRef.current.on('data', onData)
      } else {
        rmsRef.current.off('data', onData)
      }
    }
  }, [audioContext, isTTSPlaying, onData])

  const canvasWidth = useMemo(() => {
    if (!window.matchMedia("(orientation: landscape)").matches) {
      return document.body.clientWidth;
    }
    return document.body.clientWidth * 0.3;
  }, [])

  const canvasHeight = useMemo(() => {
    if (!window.matchMedia("(orientation: landscape)").matches) {
      return document.body.clientHeight * 0.3;
    }
    return document.body.clientHeight * 0.6;
  }, [])


  return (
    <div className='live2d-container' >
      <div className='live2d-canvas'>
        <canvas
          id="live2d"
          width={width ? width : canvasWidth}
          height={height ? height : canvasHeight}
          className="live2d"
          color="#f5f5f9"
        />
      </div>
    </div>
  )
}

export default ReactLive2d;

在上述代码通过React组件设置了一个元素,并通过Live2D的JavaScript API初始化和控制Live2D模型的加载、显示和动作,实现了在Web页面上展示Live2D动画形象的功能。

但是其中有些代码是根据实际业务增加的,不属于原本demo的逻辑,这边做一些简述,防止读者混淆。

// 
const onData = useCallback((data) => {
    LAppLive2DManager.getInstance().setRmsToValue(data[0])
  }, []);
// AudioRMS()用于处理音频的技术,实现音频信号的实时分析和处理
// 这段代码的目的是在确认音频源 source 存在的情况下,将它连接到通过 rmsRef 引用的目标的 input 上。
// 这样的操作在音频处理、音频可视化等场景中非常常见,比如连接一个音频源到一个用于计算实时音频信号强度的节点上。
  useEffect(() => {
    if (audioContext) {
      if (!rmsRef.current) {
        rmsRef.current = AudioRMS(audioContext, 'sqr')
      }
      if (isTTSPlaying) {
        rmsRef.current.on('data', onData)
      } else {
        rmsRef.current.off('data', onData)
      }
    }
  }, [audioContext, isTTSPlaying, onData])

// 。它调用source的connect方法,将音频源连接到rmsRef.current.input。
// 这里,rmsRef是一个引用(通过useRef创建),指向RMS处理器的实例,而rmsRef.current.input是RMS处理器的输入端。
  const configRmsWithAudio = () => {
    if (source) {
      source.connect(rmsRef.current.input)
    }
  }
// 这段代码的目的是确保每当音频上下文(audioContext)或音频源(source)发生变化时,都会更新音频源与RMS处理器之间的连接。
  useEffect(() => {
    configRmsWithAudio();
  }, [audioContext, source])

3. 引用

上述工作做完后,引用则变得很简单了。

import ReactLive2d from '...'

...

// 以下参数根据实际情况自行填写即可
  <ReactLive2d
    source={bufferSource}
    live2dModelId={'LH1'}
    isTTSPlaying={iatStatus === 'ttsPlaying'}
    audioContext={ttsRecorder.audioContext}
  />
  }, [audioContext, source])

3. 引用

上述工作做完后,引用则变得很简单了。

import ReactLive2d from '...'

...

// 以下参数根据实际情况自行填写即可
  <ReactLive2d
    source={bufferSource}
    live2dModelId={'LH1'}
    isTTSPlaying={iatStatus === 'ttsPlaying'}
    audioContext={ttsRecorder.audioContext}
  />

然后运行引用<ReactLive2d/>的界面上,便可以看到live2d的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/779437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mipi协议中的calibration和scramble模式

在MIPI(Mobile Industry Processor Interface)协议中,calibration(校准)和scramble(加扰)模式是两个重要的特性,它们分别用于优化数据传输的准确性和减少信号干扰。以下是对这两个模式的详细解析: Calibration(校准)模式 目的与功能: 校准模式主要用于优化和补偿由…

冯诺依曼体系结构与操作系统(Linux)

文章目录 前言冯诺依曼体系结构&#xff08;硬件&#xff09;操作系统&#xff08;软件&#xff09;总结 前言 冯诺依曼体系结构&#xff08;硬件&#xff09; 上图就是冯诺依曼体系结构图&#xff0c;主要包括输入设备&#xff0c;输出设备&#xff0c;存储器&#xff0c;运算…

RabbitMQ快速入门 - 图像化界面的简单操作

目录 1、RabbitMQ的安装 2、RabbitMQ基本介绍 3、简单案例 4、数据隔离 1、RabbitMQ的安装 官网链接&#xff1a;rabbitmq官网 &#xff08;官网很详细&#xff0c;也可以在官网学习啦~&#xff09; 基础入门&#xff1a;自主学习&#xff1a;最新版本&#xff1a;安装我…

【3D->2D转换(1)】LSS(提升,投放,捕捉)

Lift, Splat, Shoot 这是一个端到端架构&#xff0c;直接从任意数量的摄像头数据提取给定图像场景的鸟瞰图表示。将每个图像分别“提升&#xff08;lift&#xff09;”到每个摄像头的视锥&#xff08;frustum&#xff09;&#xff0c;然后将所有视锥“投放&#xff08;splat&a…

ubuntu下载Nginx

一、Nginx下载安装&#xff08;Ubuntu系统&#xff09; 1.nginx下载 sudo apt-get install nginx2.nginx启动 启动命令 sudo nginx重新编译(每次更改完nginx配置文件后运行&#xff09;&#xff1a; sudo nginx -s reload3.测试nginx是否启动成功 打开浏览器访问本机80端口…

【Python进阶】函数的扩展

函数 目录 函数 一、容器知识补充 1、字典遍历方法 2、遍历字典元素 keys()方法&#xff1a; values()方法&#xff1a; items()方法&#xff1a; 3、公共运算符 4、公共方法 二、函数介绍 1、函数的概念 2、引入函数 3、函数定义与调用 4、函数的参数 5、函数…

[Leetcode 128][Medium] 最长连续序列

目录 题目描述 整体思路 具体代码 题目描述 原题链接 整体思路 首先看到找连续升序排序的最长序列长度&#xff0c;想到对数组进行排序预处理。但是排序算法时间复杂度需要O(nlogn)&#xff0c;题目要求时间复杂度为O(n)。因此不能进行排序与处理 接着想到数据结构哈希表&a…

3ds Max渲染曝光过度怎么办?

3dmax效果图云渲染平台——渲染100 以3ds Max 2025、VR 6.2、CR 11.2等最新版本为基础&#xff0c;兼容fp、acescg等常用插件&#xff0c;同时LUT滤镜等参数也得到了同步支持。 注册填邀请码【7788】可领30元礼包和免费渲染券哦~ 遇到3ds Max渲染过程中曝光过度的问题&#xf…

【文献解析】一种像素级的激光雷达相机配准方法

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

openssh版本升级实战(修补ssh漏洞)基于RedHat8.4版本测试--已成功升级

升级前具有漏洞的的版本 通过命令查看目前系统的ssh和sshd版本&#xff1a; ssh -V sshd -V 注意&#xff1a;由于ssh是远程连接服务器的功能&#xff0c;在进行下面操作升级openssh前&#xff0c;请打开多个连接会话保持&#xff0c;如升级失败&#xff0c;可通过已连接的会话…

C#——密封类详情

密封类 密封类是密封方法的扩展&#xff0c;用于确保某个类不会被继承。在C#中&#xff0c;你可以使用sealed关键字来声明一个密封类。 public sealed class SealedClass {// 类成员定义 } 如果使用密封类继承的话&#xff0c;程序会报错&#xff01;&#xff01;&#xff0…

windows server2016搭建AD域服务器

文章目录 一、背景二、搭建AD域服务器步骤三、生成可供java程序使用的keystore文件四、导出某用户的keytab文件五、主机配置hosts文件六、主机确认是否能ping通本人其他相关文章链接 一、背景 亲测可用,之前搜索了很多博客&#xff0c;啥样的都有&#xff0c;就是不介绍报错以…

02浅谈大模型文本生成的背后逻辑

02浅谈大语言模型文本生成的背后逻辑 两个概念&#xff1a; 通俗理解大模型文本生成逻辑 假设有一个prompt&#xff1a;How are you &#xff1f;&#xff0c;输入给大模型&#xff0c;那么大模型使怎么输出&#xff1f;

nginx 主备server自动切换配置

nginx.conf 配置详情&#xff1a; #user nobody; worker_processes 1;error_log logs/error.log;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;client_max_body_size 2048m; # 设置最大上传限制为5Gproxy_b…

锂电池寿命预测 | Matlab基于改进的遗传算法优化BP神经网络的锂离子电池健康状态SOH估计

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 主要流程如下: 1、首先提取“放电截止电压时间”作为锂电池间接健康因子&#xff1b; 2、然后引入改进的遗传算法对BP神经网络的模型参数进行优化。 3、最后 NASA 卓越预测中心的锂电池数据集 B0005、B0006、B0007对…

【实战项目】:电商网站数据抓取分析||电商API数据采集

导语&#xff1a;在电商行业&#xff0c;了解市场动态和竞争对手的信息非常重要。通过抓取电商网站上的商品数据&#xff0c;我们可以进行市场分析、价格监控和产品趋势研究。本文将介绍如何构建一个系统&#xff0c;自动化抓取电商网站上的商品数据&#xff0c;并进行分析。 …

windows下编译ffmpeg 最详细教程

1 Ffmpeg下载地址&#xff1a;FFmpeg 使用命令下载 git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg 下载完成后会发现如下目录&#xff1a; 2 msys2下载地址&#xff1a;MSYS2 解压好后&#xff0c;选择一个非空路径安装&#xff0c;安装好后路径如下&#xff1a; 为…

短视频商城系统源码揭秘:架构设计与实现

在短视频平台和电商平台蓬勃发展的背景下&#xff0c;短视频商城系统应运而生&#xff0c;融合了短视频内容和电商功能&#xff0c;给用户带来了全新的购物体验。本文将揭示短视频商城系统的源码架构设计与实现&#xff0c;帮助开发者了解该系统的内部工作原理及其关键技术。 …

【密码学】密码学体系

密码学体系是信息安全领域的基石&#xff0c;它主要分为两大类&#xff1a;对称密码体制和非对称密码体制。 一、对称密码体制&#xff08;Symmetric Cryptography&#xff09; 在对称密码体制中&#xff0c;加密和解密使用相同的密钥。这意味着发送方和接收方都必须事先拥有这…

QT使用QGraphicsView绘图 重写QGraphicsObject类实现点在QPainterPath路径上移动动画效果

闲谈&#xff1a;眨眼间&#xff0c;2024年就过去了一半了&#xff0c;年前定下的计划一个都没完成&#xff0c;也是废了。天天刷龙王CP&#xff0c;倒是看得很开心。乘着有空&#xff0c;把之前学习的内容和示例先总结了。 目录 导读SVG 转QPainterPath 路径获取QPainterPath指…