最近被拉去支援紧急需求(赶在五一节假日前上线的,双休需要加班😱),参与到项目中才知道,开发的项目是微信小程序技术栈的。由于是临时支援,笔者也很久没开发过微信小程序了,所以挑选了相对独立,业务属性相对轻薄的模块参与。
其中有个营销活动(领红包🧧😁)的弹窗动画就要用到 lottie 动画。
本文就分享一下在小程序中使用 lottie 过程中遇到的问题与解决办法。
lottie 是 Airbnb 开源的一个动画库,用于在端上直接播放 AE ( Adobe After Effects)动画。
通过 bodymovin AE 插件将动画文件导出为 json 文件,lottie SDK 通过可以通过 JSON 文件直接播放动画。
具体 demos 效果可以上 LottieFiles 网站查看。
完成 AE 软件安装后,参照 Lottie Web GitHub 官方文档 完成 bodymovin
插件的安装。
打开动画文件后,只需简单几步操作
① window 中选择 Bodymovie
② 选择需要导出的动画资源
③ 导出配置(小程序相关)
点击对应动画的设置
勾选 Glyphs
将用到的文字+字体导出为图形。
小程序里渲染不支持加载外部字体。
这个就会有 tree shake的效果,如果动画里没有用到的文字,做动态替换的时候就会不显示,后面会详细介绍到。
勾选 Convert expressions to keyframes
将表达式转为关键帧,因为小程序里不支持使用 eval
等动态执行脚本的能力。
修改完成后点击Save
保存配置。
④ 渲染导出 JSON 文件
最后点击 Render 按钮,导出 JSON 文件。
导出文件如下,data.json 文件就是我们需要的 JSON 文件,images 里存储的就是播放要用到的图片文件。
可以使用小程序官方封装的 lottie-miniprogram 库。
快速验证的话可以打开微信开发者工具,在点击👉🏻 demo代码片段 进行创建。
① 安装依赖
npm install --save lottie-miniprogram
② 使用
tip:开发者工具中验证的话,渲染模式需要选择 webview ,Skyline 目前还不支持调试 canvas
index.wxml
<canvas id="lottie-canvas" type="2d"></canvas>
index.js
import lottie from 'lottie-miniprogram'
Page({
onReady() {
this.createSelectorQuery().select('#lottie-canvas').node((res) => {
// 取得 canvas 节点
const canvas = res[0].node
// 设置 cavnas 画布尺寸
canvas.width = 600
canvas.height = 600
lottie.setup(canvas)
const context = canvas.getContext('2d')
const lottieInstance = lottie.loadAnimation({
loop: true, // 循环播放
autoplay: true, // 自动播放
// 本地使用 http-server 启动服务后,指定本地资源地址
path: 'http://127.0.0.1:8080/lottie-demo-sources/data.json', // 通过http 制定json资源路径
// 也可以用下面这种方式,直接传入 lottie json内容
// (需要动态替换文案就需要用到这种方式)
// animationData: {/* lottie json 格式内容 */},
// 静态资源目录,通常与 animationData 配合使用
// assetsPath: 'http://127.0.0.1:8080/lottie-demo-sources/images/',
rendererSettings: {
context,
},
})
}).exec()
}
})
我这个 demo 的效果(网上找的动画素材)如下。
下面介绍在实际业务接入使用中遇到的一些问题和解决办法。
报错信息如下,这是遇到的第一个问题(也是上面导出配置中有特别说明的)。
细看了一下文档,有特别说明,expression 表达式特性是不支持的,因此需要再导出 JSON 文件时禁用相关特性。
解决办法:导出JSON文件时,禁用掉表达式特性即可。
当然禁用后,JSON 文件大小会有所增加。
比如我这个 demo 从 40kb 增加到了 240kb(当然动画不一样,增长的大小会有所不同。有些前后可能只有1-2kb的变化)。
由于需要全屏展示,动画文件的尺寸不确定,手动只设置 canvas 尺寸会有模糊的问题。
这个通过掘金搜索了一下就得到了 lottie动画模糊问题的解决方法。
微调一下上面的代码,就可以解决模糊问题。
const canvas = res[0].node
canvas.width = 600
canvas.height = 600
// 下面是新增的代码
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = canvas.width * dpr
canvas.height = canvas.height * dpr
context.scale(dpr, dpr)
lottie.setup(canvas)
弹窗的动画需要全屏展示,因此需要设置 canvas
宽高为页面宽高。
index.wxss
#lottie-canvas{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
}
index.js,使用 wx.getSystemInfoSync
获取设备的信息
const { windowWidth, windowHeight, pixelRatio } = wx.getSystemInfoSync()
canvas.width = windowWidth * pixelRatio
canvas.height = windowHeight * pixelRatio
由于是红包,需要动态展示金额(当然也可能是不固定内容的动态标题)。
思路可以参考这篇文章知乎: 动态修改 Lottie 中的文本
可以使用固定格式的文本 ${文本}
进行替换
// 伪代码
get('sourceUrl').then((res) => {
const jsonText = res.data
const animationData = JSON.parse(jsonText.replace('${金额}', '目标金额'))
})
比如我在 demo 里加一个文字
${num}
用于替换匹配接着导出 JSON 文件。
调用方法如下
// 拉取JSON文件内容
const jsonData = await new Promise((resolve) => {
wx.request({
url: 'http://127.0.0.1:8080/json/text-replace/data.json',
success: (res) => {
resolve(res.data)
}
})
})
// 随机生成1-100元的数字,保留两位小数
const num = (Math.random() * 100).toFixed(2)
// 替换内容
const animationData = JSON.parse(
JSON.stringify(jsonData)
.replace(/\$\{num\}/g, `${num}元`)
)
lottie.loadAnimation({
// 指定json内容
animationData,
// 设置依赖的图片资源位置
assetsPath: 'http://127.0.0.1:8080/json/text-replace/images/',
// ...省略其它配置
})
效果如下
在 canvas 标签上设置 display
控制显隐,偶现会提示渲染层错误。
<canvas style="display:{{show?'block':'none'}}" id="c1" type="2d"></canvas>
解决办法,给套了一层 view
,用wx:if
控制咯。
<view wx:if="{{show}}">
<canvas id="c1" type="2d"></canvas>
</view>
现象是,非冷启动小程序的时候,动画还没播放完毕就提前结束了。
看代码log,3s的动画,播放不到 1s 就触发了 complete
事件,看现象就是一闪而逝。
const ani = lottie.loadAnimation({
// 3s 的动画
animationData,
// ...省略其它配置
})
ani.addEventListener('complete', () => {
console.log('动画播放结束')
})
解决办法
TODO:未完待续
时间匆忙,介绍的不是非常的详细,感兴趣的同学可以评论区交流。
demo
完整源码见 GitHub:lottie-demo