c++ 获得文本修改时间_Aha Lottie | 动态修改 Lottie 中的文本

a0c1704102227083e7efc7918af8c6d9.png

01. WHY

不少动画中,会带有一些业务数字,比如之前文章中提到的这段动画

ad1d6b2cc9ac60ebed2581438b312b47.png
动态文案 Lottie 动画示例https://www.zhihu.com/video/1199488346264825856


这里的「3.39%」「2.43%」以及「1000 万」都属于业务数字,所以这段动画用 gif/apng/视频都不合适,因为数字有变动时候需要重新生成,而像余额宝的七日年化可是每天都会变化的,Lottie 真是这种场景下的最佳方案


02. HOW


要实现 Lottie 的文本动态修改,需要对 Lottie 的运行机制有一定了解
简单来说,运行前设计师导出的 lottie.json 对象,会被 lottie-web 解析之后产生相应的 JS 对象,并在动画播放期间,由 JS 对象计算并修改 HTML 中相应的 svg 元素属性,从而实现动画播放

452861c96937ac5015b1bd7a9e943c1b.png


因此如果要做文本动态修改,理论上在这 3 个阶段都是可以「动手脚」的注: 这里暂时忽略了 lottie canvas 模式,最后一节会补充说明
先给出一个简单的示例,看下本人自学三天 AE 导出的一个土味 lottie 动画

e5fe28b18d2051ff489ff2b69bc12826.png
一则土味 lottie 动画https://www.zhihu.com/video/1199487982731988992


可以通过我们团队自制的 lottie 编辑器预览下效果

Alipay Design​sage.alipay.com
d582895369021c3d83a6bfe0cf11fa18.png
lottie.json 文件​gw.alipayobjects.com


03.A. 修改 lottie.json


这是最容易想到的办法,lottie.json 内部描述了动画的所有细节,自然也就包含了动画中的那段文本,如果我们能找到相应的字段进行修改,也就可以实现文本替换了,简单看下 json 就能找到重点(虽然你可能完全不知道这些 key 是什么鬼)

b961ddbeb7c8e57ab32c8290a721d0fd.png
找到 lottie.json 中的「占位符」


查找下 lottie-web api 可以发现是支持直接传入 json 对象的,那么我们在外部自己发 ajax 请求获取 json 然后替换即可,代码如下

fetch('https://gw.alipayobjects.com/os/finxbff/2d0c4a95-568f-4923-bef0-e20fca6018ca/7abc1e3d-c381-49ed-ad54-3a48366f0180.json')
  .then(resp => resp.text())
  .then(text => {
    // 简单演示替换
    const newJSON = text.replace('${文本}', '拾亿');
    lottie.loadAnimation({
      animationData: JSON.parse(newJSON),
      container: document.getElementById('app'),
      loop: true
    });
  });
https://codesandbox.io/s/lingering-microservice-fh4to?fontsize=14&hidenavigation=1&theme=dark​codesandbox.io


这种做法优点是简单粗暴,但是也有一些缺点

  • 文本替换存在一定不确定性,比如上面例子如果不是设计师在 AE 中写入 ${文本} 这样明确的占位符,这个方法可就没那么容易实现了,如果设计师配合度不高,那么这个方式可操作性就差了一些
  • 这种方式无法做到「运行时」修改,也就是 lottie 解析播放后就无法再修改文本了,比如某些场景下可能需要先播放 lottie 的前一部分,用户交互产生数据后,再替换文本播放后一部分,那么这种方式就无法满足了


03.B. 修改 JS 对象


通过修改 lottie 解析后的运行时 JS 对象,理论上一样可以修改文本,官方其实提供了相应的 API,仔细查找的话在 lottie-web 的官方文档里有提及,请看 https://github.com/airbnb/lottie-web/wiki/TextLayer.updateDocumentData
对于上面的 lottie 可以这样替换

anim.addEventListener('DOMLoaded', () => {  
  anim.renderer.elements[0].elements[0].updateDocumentData({t:'拾亿'},0); 
});
  • 第一个参数表示如何替换文本,t 表示文本内容,这里还可以传入 s 修改大小,fc 修改颜色
  • 第二个参数表示替换指定关键帧的文本,文本可以有关键帧,在动画过程中展示不同的文本,如果没有关键帧可以省略这个参数


不过这个方式的麻烦之处在于,需要对解析出的 JS 对象比较了解,比如上面例子里需要知道 elements[0].elements[0] 对应的是那段文本,这里有一个人肉查找的过程
幸而我们发现官方还提供了 lottie-api 这样一个神奇的运行时修改 lottie 的库,可惜文档缺失、甚至打包有问题,导致用的人也少,这篇文章甚至可能是中文世界里第一个提到这个库的
但是这个库着实强大,比如要寻找相应的 JS 对象就很简单,示例如下

anim.addEventListener("DOMLoaded", () => {
  const api = lottie_api.createAnimationApi(anim);
  const elements = api.getKeyPath("comp1,textnode");  // 查找对象
  elements.getElements()[0].setText("拾亿");
});


这里 getKeyPath 的参数 comp1,textnode 是什么呢?看一下设计师的 AE 源文件就知道了

35fe374455a54398fba8cda21d494952.png
AE 截图

513677d0f7c791da30a50bb958ecdbd6.png
AE 截图


其实就是图层名称的拼接,这个 lottie 内部有两层结构,第一层是「合成」,名称叫 comp1,文本位于 comp1 的内部,名称叫做 textnode,于是逗号拼接下就可以找到了
elements.getElements() 可以获取到所有满足条件的 JS 对象,因为 lottie 中图层的名称并不唯一,有时候可能会找到多个对象,最后通过 setText 就可以简单修改文本了
这个方式我个人比较喜欢,因为通过看 AE 图层名称去拼接 keypath 比自己用 [0][1] 去一层层查找对象要方便和健壮很多,不过如果你没有 AE 怎么办?上面提到的我们团队的lottie 在线编辑器里,也是可以看到这些信息的,后续我们会再完善,提供一键查看 keypath 功能
最后图层名称其实也可以是中文,一样可以查找到

3c10f323a0a20276b919bd84646eefe1.png
在线 Lottie 编辑器「洛丽塔」


完整代码示例

https://codesandbox.io/s/hardcore-goldwasser-e0el3?fontsize=14&hidenavigation=1&theme=dark​codesandbox.io

注意: 由于 lottie-api 1.0.2 build 的问题,导致 import 时需要这样写import * as lottieAPI from "lottie-api/dist/lottie_api";才能正确引入,而 codesandbox 并不支持所以示例里用了 script 引入
这种方法相比第一种要繁琐一些,但是我更偏好这种,因为能够做到「运行期」替换
另外如果文本图层存在多个关键帧的话,setText() 方法可以传入第二个入参替换指定关键帧中的文案,这与 updateDocumentData 一致,因为底层调用的就是 updateDocumentData


03.C. 修改 svg 元素


这里需要提到 Lottie 中的一个鲜为人知的黑科技,如果设计师在 AE 图层命名时尾部加入 #xxx ,那么生成的 svg 元素就会有一个 id 属性为 xxx,比如

5008f0e998ea25e4781a739786bc0ca5.png
AE 截图,图层名称增加 #

8190a83050a76aff27d504f8226f93b8.png
DOM 截图,svg 元素有 id 属性


于是根据这个黑科技,我们可以这么写

anim.addEventListener("DOMLoaded", () => {
  const element = document.getElementById("J_txt");
  element.querySelector("tspan").innerHTML = "拾亿";
});


代码示例

silly-water-woj0j - CodeSandbox​codesandbox.io
80ea009ce1c5a526562f89094a1e5e30.png


这个方式有些黑科技,但是却是简单有奇效,但是这种方式有一些缺点,如果文本图层存在多个关键帧,那么这种方式比较难解决

04. 比较


综合比较三种方式,我更喜欢修改 JS 对象,因为基于中间产物的修改方式扩展性是最强的,比如能够支持运行期,还可以修改大小、颜色、替换指定关键帧等等


05. canvas 模式的问题


最上面提到先忽略 canvas 模式,那么这个模式有什么问题吗?这里又涉及到 lottie 的底层实现了
由于在 canvas 模式下绘制文本是很慢的,作者考虑后放弃了 drawText 的实现(https://github.com/airbnb/lottie-web/issues/250),转而在导出 json 时抽取字体库中的字形(Glyphs)路径放入文件,canvas 模式下通过路径绘图来画出文本
所以上述方案中的 C 肯定无法支持 canvas 模式,而 A/B 要支持则依赖于导出 JSON 时,要替换的文本字形是否已经存在于 JSON 中,因此从实际出发,一般 canvas 模式下就比较难实现替换文本了(鬼知道产品经理要替换成什么)
另外根据上面的原理,上面所有例子中,从 AE 导出 lottie 时都记得去掉「Glyphs」这个选项,并且指定具体的 font family,记得把这件事告诉你的设计师小伙伴

6df4adb3ea3c0911e2896877f6e3a27d.png
AE bodymovin 插件选项

3d9123b1d5ca4b34ff1d936a6330a804.png
AE bodymovin 插件选项


最后,下一篇预告,相信你应该能想到,动态替换 Lottie 中的图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值