Lottie-web 实践与应用
前言
- 上一期根据源码对 lottie 做了分析,今天我们结合 lottie-web 文档来说具体的使用和开发中遇到过的坑。
- 我的初步思路是这样的,开发一个基于 lottie-web 的 react 基础组件,把动画组件作为一个子组件放入弹窗组件中去应用。接下来我们就开始造轮子。
Lottie-web 使用和常用方法
我们先来讲讲 lottie 的常用方法。
基本用法
1const anim = window.bodymovin.loadAnimation({
2 container: element, // 挂载动画的容器dom元素
3 renderer: \'svg\', // 渲染方式,svg、canvas、html(轻量版仅svg渲染)
4 loop: true, // 是否循环播放
5 autoplay: true, // 是否自动播放
6 path: animJsonPath, // 动画json文件路径
7});
常用方法
lottie-web
提供了很多控制动画的方法,我们沿用上面的animation
对象来演示。
1
2anim.play(); // 播放动画,从目前停止的帧开始播放
3anim.stop(); // 停止播放动画,回到第0帧
4anim.pause(); // 暂停动画,在当前帧停止并保持
5
6anim.goToAndStop(value, isFrame); // 跳到某个时刻/帧并停止。isFrame(默认false)指示value表示帧还是时间(毫秒)
7anim.goToAndPlay(value, isFrame); // 跳到某个时刻/帧并进行播放
8anim.goToAndStop(20, true); // 跳转到第20帧并停止
9anim.goToAndPlay(200); // 跳转到第200毫秒并播放
10
11anim.playSegments(arr, forceFlag); // arr可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段
12anim.playSegments([10,30], false); // 播放完之前的片段,播放10-30帧
13anim.playSegments([[0,10],[20,30]], true); // 直接播放0-10帧和20-30帧
14
15anim.setSpeed(speed); // 设置播放速度,speed为1表示正常速度
16anim.setDirection(direction); // 设置播放方向,1表示正向播放,-1表示反向播放
17anim.destroy(); // 销毁动画,移除相应的元素标签等。在unmount的时候,需要调用该方法
常用事件
我们在 lottie-web 中可能也需要监听一些事件,比如动画相关的 dom 已经被添加到 html 后触发 的DOMLoaded
事件。监听方法如下:
1anim.addEventListener(\'DOMLoaded\', () => {
2 if (typeof callback === \'function\') {
3 callback(anim); // 处理动画内交互逻辑
4 }
5});
除了DOMLoaded
事件,下面还有一些其他常用的事件可以监听:
- complete: 播放完成(循环播放下不会触发)
- loopComplete: 当前循环下播放(循环播放/非循环播放)结束时触发
- enterFrame: 每进入一帧就会触发,播放时每一帧都会触发一次,stop 方法也会触发
- segmentStart: 播放指定片段时触发,playSegments、resetSegments 等方法刚开始播放指定片段时会发出,如果 playSegments 播放多个片段,多个片段最开始都会触发。
- data_ready: 动画 json 文件加载完毕触发
- DOMLoaded: 动画相关的 dom 已经被添加到 html 后触发
- destroy: 将在动画删除时触发
JSON数据动态更新
要实现 Lottie 的文本动态修改,需要对 Lottie 的运行机制有一定了解,简单来说, lottie-web 解析 JSON 之后产生相应的 JS 对象,并在动画播放期间,由 JS 对象计算并修改 HTML 中相应的 svg 元素属性,从而实现动画播放,这里我们只针对 SVG 类型说明。lottie原理可参考结合Lottie-web源码的深入分析。
graph LR loadAnimation --> AnimationItem --> SVG
react-lottie 实现源码
考虑到通用性,我需要考虑传入存在可变的配置参数,比如动画 JSON、控制动图弹窗、回调函数等。
1import React, { useEffect, useRef } from \'react\';
2import lottie from \'lottie-web\';
3
4export interface LottieType {
5 animJson: any; // 动画 JSON
6 setShow: Function; // 控制弹窗的显示
7 showMark?: boolean; // 多层动画时,控制弹层蒙层
8 callback: Function; // 回调函数
9}
10
11const Lottie = (props: LottieType) => {
12 const {
13 animJson,
14 setShow, // 控制关闭
15 callback,
16 showMark = false, // 控制显示蒙层
17 } = props;
18 const lottieRef = useRef(null);
19 let anim = null;
20 useEffect(() => {
21 if (lottieRef && lottieRef.current && !showMark) {
22 lottieRef.current.parentElement.parentElement.parentElement.parentElement.parentElement.previousElementSibling.remove();
23 }
24 if (animJson) {
25 anim = lottie.loadAnimation({
26 container: lottieRef.current, // the dom element that will contain the animation
27 renderer: \'svg\',
28 loop: false,
29 autoplay: false,
30 animationData: animJson,
31 rendererSettings: {
32 progressiveLoad: true,
33 preserveAspectRatio: \'xMidYMid slice\',
34 imagePreserveAspectRatio: \'xMidYMid slice\',
35 },
36 });
37 anim.addEventListener(\'DOMLoaded\', () => {
38 if (typeof callback === \'function\') {
39 callback(anim); // 处理动画内交互逻辑
40 }
41 });
42 anim.onComplete = () => {
43 setShow(false);
44 };
45 }
46 return () => {
47 anim && anim.destroy();
48 };
49 }, []);
50 return <div className=\"lottie-container\" ref={lottieRef}></div>;
51};
52
53export default Lottie;
动态更新动画 JSON 的基类
animationActionBase.m.ts
基类里提供动画操作的类型接口、加载动画的基类方法以及动态修改动画 JSON 内容的方法。
代码如下:
1
2/**
3 * 基类动画接口
4 */
5export interface AnimationActionBaseType {
6 initPopLoad: Function; // 初始化动画弹窗和动画组件
7 complete?: Function; // 自动以完成动画后操作
8 loadDataPreUpdate: Function; // load动画数据前更新数据,返回修改后的数据
9 loadedAnimationCallBack: Function; // 加载完动画后操作
10}
11/**
12 * 加载动画基类
13 * @param props
14 * initPopLoad: 初始化动画弹窗和动画组件
15 complete?: 自动以完成动画后操作
16 loadDataPreUpdate: load动画数据前更新数据,返回修改后的数据
17 loadedAnimationCallBack, 加载完动画后操作
18 */
19export const animationActionBase = (props: AnimationActionBaseType) => {
20 const {
21 initPopLoad,
22 complete = null,
23 loadDataPreUpdate,
24 loadedAnimationCallBack,
25 } = props;
26 if (typeof loadDataPreUpdate === \'function\') {
27 const data = loadDataPreUpdate();
28 const callback = (anim) => {
29 if (typeof loadedAnimationCallBack === \'function\')
30 loadedAnimationCallBack(anim);
31 if (typeof complete === \'function\') complete(anim); // 手动控制完成后的操作
32 };
33 if (typeof initPopLoad === \'function\') initPopLoad(data, callback); // 初始化渲染弹窗
34 }
35};
36
37/**
38 * 修改动画图层layers中的文案
39 * @param layers layers object
40 * @param index 下标
41 * @param value 内容
42 */
43export const setLayersText = (layers, index, value) => {
44 layers[index].nm = layers[index].t.d.k[0].s.t = value;
45};
46/**
47 * 修改动画assets里的图片数据
48 * @param assets assets object
49 * @param index 下标
50 * @param imgUrl 图标地址 不修改地址传 null
51 * @param imgFile 图片名称(带扩展名)
52 */
53export const setAssetImg = (assets, index, imgUrl, imgFile) => {
54 if (imgUrl) assets[index].u = imgUrl;
55 assets[index].p = imgFile;
56};
57
58/**
59 * 批量更新assets里的图片 URL
60 * @param assets assets object
61 * @param imgUrl 图片 URL
62 * @param version 版本清理 CDN 缓存
63 */
64export const setAssetListImgUrl = (assets, imgUrl, version = 1) => {
65 assets.forEach((item) => {
66 item.u = imgUrl;
67 item.p = item.p.split(\'?\')[0] + `?v=${version}`;
68 });
69};
参考文献
- lottie-web: github.com/airbnb/lot.…
上一篇:
结合Lottie-web源码的深入分析
下一篇:
JavaScript设计模式
相关笔记