上几期写过关于结合Lottie-web源码的深入分析以及 lotite-web实践与应用,这里就不再介绍 lottie 了,感兴趣的朋友可以看看这两篇文章。
如果在React项目直接使用 lottie-web 也不是不可以,但是,为了提高复用率,使用简单方便,所以还是有必要的, 废话不多说,直接开干...
React 版组件封装
首先需要安装 lottie-web 渲染的库
yarn add lottie-web
新建 index.tsx 组件页面,代码如下
import React, { useRef, useEffect, useMemo, forwardRef, useImperativeHandle, Ref } from 'react';
import lottie, { AnimationItem } from 'lottie-web';
// 渲染类型
type rendererType = 'svg' | 'canvas' | 'html';
/**
* 参数属性
*
* @interface IProps
*/
interface IProps {
renderer?: rendererType; // 设置渲染器类型
loop?: boolean; // 是否循环
autoPlay?: boolean; //是否自动播放
animationData?: Record<string, any>; // AE 或者 Lottie 导出的JSON数据
imageAssetsFolder?: string; // 从animationData获取数据时,调整assets图片路径
path?: string; // AE或者Lottie导出的JSON文件路径。(animationData和path互斥)
rendererSettings?: rendererSettingsType;
initLoad?: Function; // 动画加载之后处理函数
}
/**
* rendererSettings options
*
* @interface rendererSettingsType
*/
interface rendererSettingsType {
width?: number;
height?: number;
context?: CanvasRenderingContext2D; // canvas 上下文, only canvas 2d
scaleMode?: string; // 刻度模式 noScale
clearCanvas?: boolean; // 是否清除画布
progressiveLoad?: boolean; // Boolean, only svg renderer, loads dom elements when needed. Might speed up initialization for large number of elements.
hideOnTransparent?: boolean; // Boolean, only svg renderer, hides elements when opacity reaches 0 (defaults to true)
preserveAspectRatio?: string; // viewBox属性,主要用于控制viewBox的位置,或者说viewbox的对齐方式 , ,x,y 分别各有3种值xMin、xMid、xMax、yMin、yMid、yMax
className?: string;
}
export default forwardRef((props: IProps, ref: Ref<any>) => {
const {
renderer = 'svg',
loop = true,
autoPlay = true,
animationData,
path = '',
imageAssetsFolder,
rendererSettings,
initLoad,
} = props;
const refContainer = useRef(null); // 动画渲染容器
const refAnimation = useRef<AnimationItem | null>(null); // 对外输出的ref 对象
useImperativeHandle(ref, () => ({
init: () => refAnimation.current,
})); // 指定父级组件调用暴露的 ref对象,方便控制动画
let cacheAnimationOptions: any = useMemo(() => {
const options: IProps = {
loop,
renderer,
autoPlay,
rendererSettings,
};
if (animationData) {
options.animationData = animationData;
if (imageAssetsFolder) setImageAssetsFolder(options.animationData.assets, imageAssetsFolder);
} else {
options.path = path;
}
return options;
}, [loop, renderer, animationData, path]);
useEffect(() => {
if (!refContainer.current) return;
// 渲染动画
const lottieAnimationItem: AnimationItem = lottie.loadAnimation({
container: refContainer.current,
...cacheAnimationOptions,
});
if (typeof initLoad === 'function') initLoad(lottieAnimationItem);
// 渲染后的动画实例对象复制给 refAnimation.current,返回
refAnimation.current = lottieAnimationItem;
return () => {
if (!refContainer.current) return;
refAnimation.current = null; // 重置下
cacheAnimationOptions = null;
lottieAnimationItem.destroy(); // 避免内存泄漏
};
}, [refContainer.current, cacheAnimationOptions, initLoad]);
const width = rendererSettings?.width ? rendererSettings?.width + 'px' : '100%';
const height = rendererSettings?.height ? rendererSettings?.height + 'px' : '100%';
return <div ref={refContainer} style={{ width: width, height: height }}></div>;
});
/**
* 批量更新assets里的图片目录
* @param assets assets object
* @param imgUrl 图片目录|path
*/
const setImageAssetsFolder = (assets: Array<{ u: string; p: string }>, imgUrl: string) => {
const t = new Date();
assets.forEach((item) => {
if (item.u === imgUrl || item.p.indexOf('?t=') !== -1) return;
const img = new Image();
item.u = imgUrl;
item.p = item.p + `?t=${t.getTime()}`;
img.src = imgUrl + item.p; // 预加载
});
};
从代码了可以看出,有个 setImageAssetsFolder 函数是处理动画文件里图层图片资源路径以及预加载,另一种方案就是把 asset 里所有图片资源合并变成 base64为一张图,不过前提是得有base64 编码的图片集才行,赶兴趣的同学可以试试。
Demo 示例
demo.tsx
import React, { useEffect, useState, useRef, useCallback } from 'react';
import Lottie from './index';
import lottie_api from 'lottie-api';
export default () => {
const [visible, setVisible] = useState(false);
const lottieRef = useRef(null);
const lottieAction = useCallback(
(type: string) => {
if (!lottieRef.current) return;
const lottieAnimation = lottieRef.current.init();
if (type === 'play') {
// 从当前状态继续向前播放
lottieAnimation.play();
} else if (type === 'pause') {
lottieAnimation.pause(); //暂停
} else if (type === 'stop') {
// 停止动画,恢复到初始状态
lottieAnimation.stop();
}
},
[lottieRef],
);
// 动画内的交互操作,均可在此处处理
const addListener = useCallback((lottieAnimation) => {
lottieAnimation.addEventListener('DOMLoaded', () => {
const api = lottie_api.createAnimationApi(lottieAnimation);
// 可以通过 api 给目标元素增加监听事件,这里就不演示
});
}, []);
return (
<>
```
<div title="控制动画">
<button onClick={() => lottieAction('play')}>
播放
</button>
<button onClick={() => lottieAction('pause')}>
暂停
</button>
<button onClick={() => lottieAction('stop')}>
停止
</button>
<div style={{ width: '200px', height: '200px' }}>
<Lottie
ref={lottieRef} path="https://assets10.lottiefiles.com/packages/lf20_a3emlnqk.json"
rendererSettings={{ className: 'lf20_a3emlnqk', width: 200, height: 150 }}
initLoad={addListener}
/>
</div>
</div>
</>
)
}
效果
文档说明
Lottie 组件可以通过设置该组件的 ref,通过 ref.current 上挂载了 init 方法,它返回动画实例对象。通过该实例对象可以对动画进行相关操作,具体详情请参考官方 usage api
官方还提供了神奇的lottie-api运行时修改 lottie 的库,可惜文档缺失,用的人并不多,但是功能很强大。测试 demo 里有相关示例。该库续根据项目需要是否安装即可。
IProps
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| renderer | 设置渲染器类型 ,分别'svg', 'canvas' ,'html' 3 种类型 可选 | string | 'svg' |
| loop | 是否循环 可选 | boolean | true |
| autoPlay | 是否自动播放 可选 | boolean | true |
| animationData | AE 或者 Lottie 导出的动画 JSON 数据 可选 | Record<string,any> | true |
| imageAssetsFolder | imageAssetsFolder 情况,设置带图的 assets 目录 可选 | string | -- |
| path | AE 或者 Lottie 导出的 JSON 文件路径。(animationData 和 path 互斥) 可选 | string | '' |
| rendererSettings | 画布设置 可选 | rendererSettingsType | -- |
| initLoad | 渲染前调用函数,函数参数:动画对象 ,动画的事件监听可在这里处理 可选 | Function | -- |
rendererSettingsType
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| context | canvas 上下文 可选 | CanvasRenderingContext2D | -- |
| scaleMode | 刻度模式 可选 | string | |
| clearCanvas | 是否清楚画布 可选 | boolean | |
| progressiveLoad | 仅 svg 渲染器,在需要时加载 dom 元素。 可能会加快大量元素的初始化。 可选 | boolean | -- |
| hideOnTransparent | 仅 svg 渲染器,当不透明度达到 0 时隐藏元素 可选 | boolean | true |
| preserveAspectRatio | 参考下面参说明 可选 | string | 'xMidYMid meet' |
| className | 自定义样式 Name 可选 | string | -- |
| width | svg 宽 并且父级元素同宽 可选 | number | -- |
| height | svg 高 并且父级元素同高 可选 | number | -- |
preserveAspectRatio
viewBox 属性,主要用控制 viewbox 的对齐方式,其值为空格分隔的两个值组合而成;第 1 个值表示,viewBox 如何与 SVG viewport 对齐;值由 x,y 分别各有 3 种值 xMin、xMid、xMax、yMin、yMid、yMax 组合而成;第 2 个值表示,如何维持高宽比,三种类型:
- meet 保持纵横比缩放 viewBox 适应 viewport
- slice 保持纵横比同时比例小的方向放大填满 viewport
- none 扭曲纵横比以充分适应 viewport
AE设计师——注意事项
支持度和兼容性方面的重要建议
- 不使用表达式和特效。lottie 暂不支持。
- 注意遮罩尺寸。若使用 alpha 遮罩,遮照的大小会对性能产生很大的影响。尽可能地把遮罩尺寸维持到最小。
- 不使用混合模式和亮度蒙版。
- 描边动效不支持,因为这个属性会导致 lottie 性能问题,所以后来 lottie 去掉了对该属性的支持。
- 设置空白对象。若使用空白对象,需确保勾选可见并设置透明度为 0% ,否则不会被导出到 json 文件。
- Matte 和 mask 有尺寸问题,使用半透明遮罩会影响性能,可能会造成潜在的渲染错误以及严重的性能损耗!很多设计师习惯画图形用纯色图层,纯色图层形成的形状是基于蒙版的,这个要注意。如果必须使用遮罩,请覆盖最小的区域。
- lottie 不支持图层样式,图层效果不支持 drop shadow, color overlay 或 stroke。
- lottie 不支持合成路径(merged path),如果从 AI 中制作的图形有,请删掉。
- 图层命名尽量用英文。如果你使用了渐变,那么工程名(包括 aep 文件名)以及渐变图层、到其父级图层、到根级图层这条链路中的每一层都要用英文,包括变换属性命名,否则会出现渲染错误。
兼容性上的建议
- 滤镜效果在客户端上不全支持;颜色滤镜的试用对客户端的运行上存在额外的性能损耗。
- 图层如果存在 “自动定向” 特性,在 Web 和 Android 上不支持。
- 不推荐使用“遮罩层”,遮罩层对客户端运行性能的损耗极大,建议避免使用。如果可以用蒙版效果替代,建议用蒙版替代使用。
- 蒙版的使用对客户端运行性能消耗比较大,如果存在蒙版为“非必需”的,务必删除。
- 图层如果使用 “时间重映射” 特性,在 iOS 上不支持。
- 矢量的形状在客户端运行时存在性能的损耗。尤其是在形状图层数量比较大的情况下。如果一个形状无形变要求,可以考虑转化成 png 图片替代,以提高运行性能。
- 建议用“形状”图层替代“纯色”;纯色模块自带一个无意义的“蒙版”,对客户端运行有不必要的性能损耗,量大时引起客户端卡顿;
性能方面的建议
- 动画简单化。创建动画时需时刻记着保持 json 文件的精简,比如尽量不使用占用空间最多的路径关键帧动画。诸如自动跟踪描绘、颤动之类的技术会使得 json 文件变得非常大且耗性能。
- 如果有循环的帧,请不要在动画文件里面循环,请数出帧数,让开发自行控制这段动画的循环,能节省相同图层和动画的体积。
- 建立形状图层。将 AI、EPS、SVG 和 PDF 等资源转换成形状图层否则无法在 lottie 中正常使用,转换好后注意删除该资源以防被导出到 json 文件。
- 设置尺寸。在 AE 中可设置合成尺寸为任意大小,但需确保导出时合成尺寸和资源尺寸大小保持一致。
- 在尽量满足效果的情况下,请对路径做适当的裁剪,这个对性能影响很大。
- lottie 进行动画的时候会按照 AE 的设计进行分层,所以要尽量减少层数。
- 若确实没有必要使用路径动画,请将矢量图形替换为 png 图片,并用 transfrom 属性完成动画。
- 可以根据实际状况,斟酌降低动画帧率或者减少关键帧数量,这会减少每秒绘制的次数。
- 精简动画时长,可以循环的动作,就不要在时间轴做两遍,每一次读取关键帧都会消耗性能。编排上尽量避免 a 动作结束,b 动作开始,可以让动作有所重叠,减少动画长度。
- 同类项合并,有些元素是相似的,或者相同的用在了不同的地方,那就把这个元素预合成重复使用这一个元件,可以通过对该预合成的动画属性的调整达到想要的动画效果。
- 尽量减少图层个数。每个图层都会导出成相应的 json 数据,图层减少能从很大程度上减小 json 大小。
- 尽可能所有的图层都是在 AE 里面画出来的,而不是从其他软件引入的。如果是其他软件引入的,很可能导致描述这个图形的 json 部分变得很大。
- 制作的时候,请将动画元素铺满整个画布,这样可以避免浪费,也方便前端进行尺寸的调整。
- 如果矢量图形是在 AI 中导出的,请将多余的“组”等没有任何实际效用的元素删掉。
- 删除那些关闭了和无用的属性。
- 只导出 1x 图。
- 为了防止 lottie 导出的兼容性问题,请尽量使用英文版本 AE ,图层需简洁,命名清晰
- 避免大面积矢量部分,以及大面积粒子效果
- 避免使用 3D 等高性能损耗样式
最后
以上就是本文全部内容,希望这篇文章对大家有所帮助,有不到之处欢迎在评论区指正交流你的想法和心得,欢迎一起探索前端。