Web-Editor
前言
动画需求在业务开发中是很常见的功能,无论是客户端开发、Web 开发、还是桌面端开发,为了产品有更好的用户体验,UED 设计的视觉效果也愈发的复杂,一般些简单的淡入淡出,旋转效果开发花费些时间即可搞定,甚至稍微复杂些的动画多花费些时间也能完成,但主要面临几个问题:
- 耗时成本太高:在开发工作量很多的情况下,对开发来说无疑是增加了一大波工作量,用身边的一些同事的说法就是,连正常的业务需求都开发不完,谁还去精雕细琢那些去;
- 吃力不讨好:开发吭哧吭哧写完了动画交予 UED、PD 去验收时很多时候会反馈说这实现的和设计稿差距有些多,很高概率会有多次的反工。
自己手写动画太费事,那就直接用一劳永逸的方法,UED 直接出 GIF 动画,开发直接贴 GIF 图片即可,对于开发开说几个小时的动画需求几分钟即可搞定,整体来说 GIF 是一种制作简单、来源广泛(很多软件都可以生成gif动画)、兼容性强(基本上所有浏览器都支持)的轻量级的方案。
但从产品的视角、用户视角来说 GIF 的动画的弊端也是非常多的,诸如:
- GIF 图片保存了每一帧的内容,因此造成图片很大;为解决图片过大的问题,大多数情况下都会采用压缩的方式较小体积,但这也造成了动画模糊、失真;
- 用户无法直接与动画本身做交互,如停止、开始、加速等交互,也无法直接修改动画元素的属性如背景色等;
既如此,那就再引入一种新的技术方案 Lottie , 感兴趣的可以先先睹为快 Lottie-Example 。
Lottie
什么是 Lottie
Lottie 是 Airbnb 发布的一款开源动画库,它适用于 Android、iOS、Web 和 Windows 的库。 它提供了一套从设计师使用 AE(Adobe After Effects)到各端开发者实现动画的工具流。
简单来说就是 UED 和开发各司其职,UED 提供动画 json 文件即可, 开发者就可以直接运用在 iOS、Android、Web 和 React Native之上,无需其他额外操作。
今天对如何设计动画不做详细介绍,只是从前端的视角出发看看如何使用 Lottie,实现精致的动画。
Lottie-Web
Lottie-Web 是 Lottie 在 web 端的技术方案。
Lottie-Web 提供了 SVG、Canvas 和 HTML 三种渲染模式,一般使用 Svg 或 Canvas 即可。
- SVG 渲染器支持的特性最多,也是使用最多的渲染方式。并且 SVG 是可伸缩的,任何分辨率下不会失真;
- Canvas 渲染器就是根据动画的数据将每一帧的对象不断重绘出来;
- HTML 渲染器受限于其功能,支持的特性最少,只能做一些很简单的图形或者文字,也不支持滤镜效果。
使用
安装依赖
npm i lottie-web --save
or
yarn add lottie-web --save
实例化
以下方法提供了一个简单的初始化过程,其他参数可参考 官网 。
function init(){
// 读取动画容器
const lottieContainer = document.querySelector("#container");
if (!lottieContainer) return;
// 实例化
const lottieInstance = lottie.loadAnimation({
// UED 提供的 动画的 json 文件
path: 'https://static-cdn.canyuegongzi.xyz/lf20/lf20_jv0xz0qi.json',
// 渲染方式
renderer: "svg",
// 是否循环
loop: true,
container: lottieContainer
});
}
至此, 开发者设计师可直接还原动画效果了,再也不会出现买家秀卖家秀的情况,也避免了开发和设计之前的互相扯皮,而且 JSON 文件,可以多端复用(Web、Android、iOS、React Native)。
动画控制
开始、结束动画
// 开始动画
function onStart() {
lottieInstance?.play();
}
// 结束动画
function onStop() {
lottieInstance?.stop();
}
速度修改
// 修改动画播放速度
function updateSpeed(val: number) {
lottieInstance?.setSpeed(val);
}
Lottie-Web 提供了相当丰富的 Api 此处借花献佛从网络拷贝了一份常用的如下,其他的可参考官网。
- animation.play():播放,从当前帧开始播放;
- animation.stop():停止,并回到第0帧;
- animation.pause():暂停,并保持当前帧;
- animation.goToAndStop(value, isFrame):跳到某个时刻/帧并停止(isFrame(可省略,默认false:毫秒;true:帧)指明value的单位是毫秒还是帧);
- animation.goToAndPlay(value, isFrame):跳到某个时刻/帧并播放;
animation.goToAndStop(30, true) // 跳转到第30帧并停止
animation.goToAndPlay(300) // 跳转到第300毫秒并播放
- animation.playSegments(arr, forceFlag):以帧为单位,播放指定片段(arr可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段);
animation.playSegments([10,20], false) // 播放完之前的片段,播放10-20帧
animation.playSegments([[0,5],[10,18]], true) // 直接播放0-5帧和10-18帧
- animation.setSpeed(speed):设置播放速度,speed为1表示正常速度;
- animation.setDirection(direction): 设置播放方向,1表示正向播放,-1表示反向播放
- animation.destroy(): 删除该动画,移除相应的元素标签等。
生命周期
Lottie-Web 提供了几个比较使用的生命周期钩子函数,开发者可根据需要做定制开发:
- data_ready:动画数据加载完毕;
- config_ready:完成初始配置后;
- data_failed:当无法加载动画的一部分时;
- loaded_images:当所有图像加载成功或错误时;
- DOMLoaded:将元素添加到DOM时。
animation.addEventListener('data_ready', () => {
console.log("data_ready")
});
进阶
上述章节中已经可以完美的运行 Lottie 动画了,但每个动画都得手动实例化,是不是很繁琐,此处笔者通过 WebComponent 技术方案做了进一步封装,方便开发者更简洁的调用, 先睹为快,快速体验 。
WuLottie 封装
import { extractClass } from '@wu-component/common';
import { h, Component, WuComponent, Prop, OnConnected, OnDisConnected, Watch } from '@wu-component/web-core-plus';
import lottie, { AnimationItem } from "lottie-web";
import css from './index.scss';
@Component({
name: 'wu-plus-lottie',
css: css,
})
export class WuLottie extends WuComponent implements OnConnected, OnDisConnected {
constructor() {
super();
}
public lottieInstance!: AnimationItem;
public lottieContainer!: HTMLDivElement;
@Prop({ type: Boolean, default: true })
public loop: boolean;
@Prop({ type: String, default: undefined })
public data: string;
@Prop({ type: Boolean, default: true })
public autoplay: boolean;
@Prop({ type: String, default: 'svg' })
public renderer: 'svg' | 'canvas' | 'html'
@Prop({ type: Object, default: {} })
public config: Record<string, any>;
public override connected(shadowRoot: ShadowRoot): void {
this.init();
}
// 元素销毁时也同步销毁掉 lottie 动画
public override disConnected(): void {
this.lottieInstance.destroy();
}
private init(){
// 实例化动画
this.lottieContainer = this.shadowRoot.querySelector('.lottieWrapper');
if (!this.lottieContainer) return;
this.lottieInstance = lottie.loadAnimation({
// @ts-ignore
...this.$reactive || {},
path: typeof this.data === 'string' ? this.data : undefined,
animationData: typeof this.data === 'object' ? this.data : undefined,
container: this.shadowRoot.querySelector('.lottieWrapper'),
});
}
@Watch('data')
public dataChnage(val: string, old: string) {
this.init();
}
@Watch('loop')
public loopChnage(val: boolean, old: boolean) {
if (!this.lottieInstance) return;
this.lottieInstance.loop = val;
if (val && this.lottieInstance.isPaused) {
this.lottieInstance.play();
}
}
@Watch('autoplay')
public lautoplayChnage(val: boolean, old: boolean) {
if (!this.lottieInstance) return;
this.lottieInstance.autoplay = val;
}
public stop() {
return this.lottieInstance && this.lottieInstance.stop();
}
public play() {
return this.lottieInstance && this.lottieInstance.play();
}
public override render(_renderProps = {}, _store = {}) {
return (
<div {...extractClass({}, 'lottieWrapper', {})}> </div>
);
}
}
如何使用
安装依赖
npm i l@wu-component/wu-lottie --save
or
yarn add @wu-component/wu-lottie --save
使用
开发者无需手动实例化 Lottie, 可如同普通 HTML 标签般接入。
<wu-plus-lottie data="https://cdn.canyuegongzi.xyz/wu-component-static/lf20_r6blppzq.json"></wu-plus-lottie>
思考
了解了如何使用 Lottie ,但是在实际应用中也有一些优势和不足,要按照实际情况进行取舍。
- Lottie-web 文件本身仍然比较大,未压缩大小为 513k,轻量版压缩后也有 144k,经过 Gzip 后,大小为39k。所以,需要注意 Lottie-web 的加载;
- 效果完全依赖设计师,结果需要开发把关,如最后的 json 文件大小;
- 部分AE特效不支持。有少量的 AE 动画效果,Lottie 无法实现,需要格外注意 supported-features 。
虽然如此,Lottie 还是相当优秀的动画方案,多种技术方案多条路嘛,大家在日常工作中都在使用什么技术方案欢迎在评论区讨论。
方案中涉及的代码均可在 github 阅读,欢迎 Star。