在现代互联网应用中,大至链路的衔接,小至一个按钮的点击响应,动效是体验无处不在的润滑剂,为用户每一步的操作提供了合理的预期与过渡。而通过动效衔接不同界面或不同响应状态,无论对流畅直观地表达流程意图,还是精雕细琢让体验更丝滑的微动效,动效设计都是应用中非常重要的一环。
在业务落地动效过程中,总是伴随着三大痛点:实现成本高、沟通成本高和性能难以保证。本文将围绕这个三个点进行剖析解决方案,提高动效的交付效率。
图1 动效演示图片

2 背景与问题
在面向C端的业务中,许多页面中都需要添加动画效果(以下统称动效),来加强页面元素的表现力。但是在动效模块的协作沟通经常会出现以下几种情况,导致添加动效会成为一个高成本的事情。同时对于已使用过的动效没有沉淀为设计资产,导致出现重复设计与开发的事情,加大了团队的投入成本。
2.1 业务场景
业务伙伴觉得界面转场不够丝滑,害怕用户在该环节造成较多的流失,想在该环节加入动效提高界面表现力,挽留用户进入下一阶段,在交付动效时,往往可能会出现一下几个情况:
情况一:找一个第三方平台的效果,设计伙伴让开发伙伴按照第三方动效GIF图片进行开发,后续进行口头沟通验收
情况二:设计伙伴与业务伙伴沟通动效内容并制作对应的MP4演示文件,给到相应的素材,让开发对照MP4进行实现,后续通过比对MP4进行效果验收
情况三:设计伙伴给到静态素材和部分文字参数信息,让开发伙伴进行组合信息实现动效内容
就此我们可以看到进行支持动效内容的接入,要处理的问题非常多,要花费很大的成本进行沟通、理解、验收,无法进行快速高效的交付动效模块。
图2 动效业务背景
2.2 带来的问题沟通成本高:在动效模块的协作中,由于缺乏统一且有效的沟通方式,导致动效的设计、开发和验收过程变得复杂,增加了沟通成本。动效设计资产未沉淀:现有的动效设计方式,使得动效无法成为可复用的设计资产,从而导致重复设计和开发,增加了团队投入成本。动效质量控制难:由于动效的复杂性和易变性,仅通过口头沟通或MP4演示文件进行验收,难以确保动效质量。
图3 动效协作中的问题
2.3 问题的根本原因
首先我们要解决高效交付动效的问题,要先找到导致这个问题的根本原因是什么?
从上述描述中不难看出,设计与开发关于动效协作主要问题发生在演示demo不完善、动效参数、素材不符合规范、验收成本较大的4个问题上。
本着发现问题,解决问题的原则。我们分析流程中每个环节,通过分析可以得到在最关键的点就是输出与输入时都不存在动效标准,设计与开发都只能对着效果图进行调试参数,从而达到预期效果,这也导致验收阶段出现开发与设计对于动效表现无法达成一致的问题。
图4 设计与前端伙伴协作流程
3 方案设计与实现
接下来就开始针对上诉问题,来思考解决方案。在讲方案内容之前,我们需要先看下目前行业内有哪些动效的格式,我们需要从中调选出符合目前公司业务和契合团队成本的动效格式。
3.1 常见的动画格式
3.1.1 GIF与APNGGIF:是在1987年由 Compu Serve 公司为了填补跨平台图像格式的空白而发展起来的。GIF 可以被 PC 和 Mactiontosh 等多种平台上被支持。
优点:高兼容性、接入成本低缺点:最高支持256种颜色、内存占用高、复杂动画文件资源较大APNG:诞生于2004年,是一个基于 png 的位图动画格式,扩展方法类似主要用于网页的 GIF 89a,仍对传统PNG保留向下兼容,2017年主流浏览器几乎都已经支持 APNG。
优点:高兼容性、接入成本低、色彩范围覆盖广,支持透明缺点:内存占用高、复杂动画文件资源较大、交互性差3.1.2 LottieLottie 由 Airbnb 推出,并且迅速在国内外各种大小厂快速推广开来,目前已经是一个非常普遍常用的格式。
优点:文件资源小、支持交互操作、支持动态更新能力缺点:3D效果支持较弱、部分效果内存占用高3.1.3 SVGA针对 Lottie 对缓动曲线解析差带来的性能问题和稳定性问题,我们会有第二种备选方案是 SVGA,不管是导出之后的内存占用,还是在各个端的表现稳定性都会好很多。
优点:文件资源小、支持交互操作、支持动态更新能力缺点:社区支持程度弱、AE支持特性较少、框架文档不完善3.1.4 MP4MP4 是一套用于音频、视频信息的压缩编码标准,由国际标准化组织(ISO)和国际电工委员会(IEC)下属的“动态图像专家组”(Moving Picture Experts Group,即MPEG)制定,第一版在1998年10月通过。目前已经是一个非常普遍常用的格式
优点:高兼容性、文件资源小、性能表现高、支持所有动画呈现缺点:不支持透明、不支持交互操作3.2 选择 Lottie 的原因3.2.1 Lottie 的优势平台兼容性好:支持H5、小程序、APP多平台,AE支持特性是大于APNG/GIF/SVGA,小于MP4文件资源小:优于GIF、APNG交互性强:对比GIF、APNG、MP4,Lottie 交互性更强,适应场景更广沉淀资产:lottie 相比其他格式,部分场景可脱离设计稿进行二次更新,更有利于资产沉淀,建设研发成本3.2.2 Lottie 的劣势内存占用高:相比 SVGA 与 MP4 动画格式,在客户端内存占用会相对较高,影响性能表现效果存在兼容性:通过 AE 制作动画只能通过 Bodymovin 导出,并且存在部分3D效果不支持的情况了解到所有的动画格式后,经过优劣势的对比,我们选择了lottie方案,选择它的理由主要是以下几点:
现有业务在使用3D效果场景较少,不需要考虑该技术点的兼容性问题团队伙伴接入 Lottie 技术成本较少 可以快速落地至业务中Lottie 支持脱离设计伙伴进行二次变更与沉淀资源,便于快速修改与复用综合性能、文件体积、交互性、可复用程度等考虑因素,Lottie 是当下较优的选择3.3 方案设计通过选定 Lottie 作为动画格式后,下面就需要围绕这个格式设计对应的方案,针对输出与输入格式不一致的核心问题,将通过以下的三个环节设计来解决。
3.3.1 三大环节
建立动效输出规范:制定统一的动效设计规范,包括动效类型、时长、颜色等,形成动效库。这将有助于降低沟通成本,提高动效的复用性。建立交互式动效平台:借助交互式动效设计平台,设计伙伴可以更直观地设计和展示动效,同时也能让业务伙伴更好地理解和参与动效设计过程。制定动效验收标准:制定明确的动效验收标准,如动效时长、播放顺序、视觉效果等,以便于验收时能有据可依,确保动效质量。图5 协作解决方案泳道图
3.3.1.1 动效输出端规范
Lottie 动效都是由 AE 软件进行制作,在 AE 中有一个 Bodymovin 插件可以将动效导出为一份 JSON 文件,这样就可以让设计伙伴导出的动效都是同一份标准。
图6 设计与前端伙伴的新协作流程
3.3.1.2 交互式动效平台
建立动效资产管理平台,可支持设计伙伴上传动效单元与预览动效效果,确认设计、开发、业务伙伴对于动效交互方式达成一致,避免重复投入的沟通成本。同时可以将已有动效进行沉淀,便于后续的快速复用,降低团队研发成本。
图7 加入平台的新协作流程
3.3.1.3 动效验收标准
在解决设计侧的输出规范,通过交互式动效平台进行导入,完成动效单元的确认后,即可按照规范输出给开发伙伴,让开发伙伴拿到符合规范的动效单元ID,完成整体流程的串联。
3.3.2 平台功能
通过上述分析,我们需要建立动效平台来管控设计动效标准、业务对齐的动效产物和开发伙伴需要的动效单元ID,下面我们就来看下动效平台需要做哪些工作,才能完成上述的方案。
接下来就让我们建设一款基于 Lottie 的动效设计平台,快速生成设计师想要的动态效果,极大地提高了设计效率和设计还原度。作为一站式动效制作平台,通过大量的动效素材以及可视化编辑能力,帮助沉淀资产快速得到复用,提高动效模块的交付效率。
3.3.2.1 需要的功能动效编辑功能颜色/图层编辑基本信息编辑动效预览功能支持动画的执行与暂停支持动画的多场景预览导入导出功能支持资产的接入与下载支持未确定的动画进行预览确认
3.3.2.2 需要的编码工作Lottie JSON 内容解析支持 layers assets shapes 多类型的解析工作Lottie preview 预览画布lottie-web 各类 api 接入完成控制器相关模块编码JSON转APNG/GIF功能支持简单类的动画可以直接投入业务使用3.3.3 平台实现下面主要是介绍针对上面的功能设计需要做的核心编码工作,由于篇幅受限我们主要讲诉部分核心模块的代码,主要是解析和渲染模块的处理逻辑。
3.3.3.1 架构图
平台架构主要分为6层,包含管理端与移动端整体环节(虚线部分表示目前开发中)。
图8 平台系统架构图
3.3.3.2 核心代码 - 解析模块
核心的内容主要集中在 JSON 内容的解析模块与模块渲染更新环节上,下面我们就来看下该模块的核心实现吧。
要编写解析模块的代码,首先我们要知道 Lottie JSON 内容本身的含义,这样我们才能针对不同类型的节点进行不同操作的编辑处理。
lottie 动画 Json 文件共分为4层结构:
结构层:可以读取到动画画布的宽高、帧数、背景色、时间、起始关键、结束帧等assets:图片资源信息集合,这里放置的是制作动画时引用的图片资源layers:图层集合,这里可以获取到图层,每个图层的开始帧、结束帧等shapes:元素集合,内部包含图层动画路径已经填充的颜色与内容等信息lottie json内容结构:"v": "5.6.10", // 使用bodymovie插件的版本"fr": 32, // 帧速率"ip": 0, // 合成开始时间"op": 64, // 合成持续时间"w": 750, // 合成宽度"h": 1334, // 合成高度"nm": "合成 1", // 合成名"ddd": 0, // 是否3d图层"assets": [] // 使用的资源"layers": [] // 图层集合"shapes": [] // 元素集合"markers": [] // 蒙层集合
获取到 lottie json 文件内容后,首先将 assets 进行转换为 object,便于后续通过refId 进行查询到对应节点,接下来进行递归解析最外层的 layers 字段,通过 refId 与 layers 判定是否存在子图层节点,并在查询过程中写入 indexKey 字段,来保证查询到每个阶段都有唯一的标识,函数执行完成后,将会得到一个 Tree Data 数据。
lottie结构解析函数:
async function resolvingLottieJson(data: string) { // 首先找到 -> layers -> refId -> assets -> layers >> 复合图层/资源图层 // 其次路径 -> layers -> layers -> shapes >> 元素图层 const info: any = JSON.parse(data) treeMeatData.value = info const assetsMap = keyBy(info.assets, 'id') let result = [] async function queryInfo(list: any, parentNodeKey: string) { const infoTree = [] for (let index = 0; index < list.length; index++) { const item = list[index] item.indexKey = `${parentNodeKey}${parentNodeKey ? '-' : ''}${index}` item.cl = `layer-inactive ${item.cl ? `${item.cl} ` : ''}class_${item.indexKey}` if (item.refId) { const itemAssets = assetsMap[item.refId] if (itemAssets.layers) item.layers = await queryInfo(itemAssets.layers, item.indexKey) } else if (item.layers) { item.layers = await queryInfo(item.layers || [], item.indexKey) } if (item.ty !== undefined) infoTree.push(item) } return infoTree } result = await queryInfo(info.layers, '') treeData.value = result return info}
通过解析函数的支持,将会得到一份 tree 的数据,下面就可以针对节点加入编辑相关的功能。
解析结构
解析数据
图9 Lottie 文件解析结构示意
3.3.3.3 核心代码 - 渲染模块
在通过解析模块为每个节点创建唯一标识与结构后,下面要处理实时编辑与更新功能,可通过两种方式来完成实时渲染和数据同步。
画布初始化渲染-初始化lottie渲染:
lottie.loadAnimation({ container: document.getElementById('preview-lottie'), renderer: 'svg', loop: lottieLoop.value, autoplay: true, animationData: data})
方式一:修改 json fb内容,来完成动态渲染
通过操作图层所挂载的 indexKey 来查询到图层节点,再通过 refId 关联的 assets 找到指定的资源信息,拿到指定字段 p 更换为指定资源链接,完成图层节点的更新。
JSON操作方式:
// 找到对应 json 节点直接替换 p 属性const asset = lottieData.assets.find(a => a.id === '7')asset.p = '/himo/light_bg.png'lottie.loadAnimation({ container: document.getElementById('preview-lottie'), animationData: json})
方式二:修改 JS 对象,来完成动态渲染
通过操作图层所挂载的 cl 字段来查询到图层渲染的 DOM 元素,再通过 setAttribute 方法来修改 DOM 节点的属性内容来实现动态更新的内容,需要注意的是该方式不适合小程序环境。
JS操作方式:
anim.addEventListener('DOMLoaded', () => { if (anim.renderer.rendererType === 'canvas') { // canvas 模式下的图片替换 anim.renderer.elements[currentEleIndex].img.src = '/himo/light_bg.png' } else { // svg 模式下的图片替换,前两个参数为固定值 anim.renderer.elements[currentEleIndex].innerElem.setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '/himo/light_bg.png' ) } })
注意点:在 canvas 模式下替换的图片需要保持与原图片尺寸一致,否则实际动画效果会有问题;svg 模式下则受益于 svg image 元素的特性,不需要如此强的约束。
3.3.3.4 核心功能 - 动态更新图片
目前已知 Lottie 具有这个三个阶段,现在可以根据不同阶段提供对应的功能。
在常见的业务场景中都有动效在生产环境运行,但此时也有业务方修改动画资源的诉求,这个时候就需要平台具有指定动效帧数进行资源编辑的功能。
图10 Lottie运行流程
由于部分动效素材是在不同的帧位置才出现,为了支持所有的素材都支持编辑操作,需要建立指定动效帧数的操作控件,选取指定帧后,对该位置的素材进行编辑操作。
指定动效帧数:建立可指定跳转帧数的控制函数,与建立进度条控件支持界面操作。下面我们来看下具体代码实现。
帧数功能代码:
<template> <div class="control-panel"> <div class="play" @click="lottiePlay"> <img :src="playIcon" alt="" /> </div> <div class="frame"> <a-slider v-if="lottieFrame > 0" :value="currentFrame" :max="lottieFrame" :tooltip-open="true" @change="framgChange" /> </div> <div class="loop" @click="setLoop"> <img :src="loopIcon" alt="" /> </div> </div></template><script lang="ts" setup> // 帧数变化响应函数 function framgChange(frameNumber: number) { // 情况一:如在播放状态 则跳转指定帧数进行播放 // 情况二:如在暂停状态 则跳转指定帧数进行暂停 if (playState.value) lottieAnimation.value.goToAndPlay(frameNumber, true) else lottieAnimation.value.goToAndStop(frameNumber, true) currentFrame.value = frameNumber }</script>
资源编辑功能:根据选中的图层节点进行创建对应的配置操作表单,通过表单控件来编辑动效资源信息。
首先根据选中的图层节点来初始化配置表单再开始监听配置表单项,识别到资源背景图片发生变化,进行 DOM 节点更新与 Lottie JSON 元数据进行同步资源编辑功能:
// 选中对应的操作图层节点async function activeLayerNode(key: any, data: any) { const item = data.node.dataRef let backgroundImage = '' if (item.ty === 2) { const assetsMap = keyBy(treeMeatData.value.assets, 'id') // 图片节点 backgroundImage = assetsMap[item.refId].p } editConfig.value = { ty: item.ty, backgroundImage } // 建立对应的编辑配置内容 activeNode.value = item}// 监听editConfig配置对象的变化watch( editConfig, async (value, oldValue) => { const assetsMap = keyBy(treeMeatData.value.assets, 'id') if (activeNode.value.ty === 2) { const image = editConfig.value.backgroundImage // 通过JS对象来更新DOM界面 const element = document.querySelector(`.class_${activeNode.value.indexKey} > image`) element.setAttribute('href', image) // 查询原始lottie数据 进行更新处理 let isQuerySuccess = false const queryInfo = async (list: any) => { for (let index = 0; index < list.length; index++) { const item = list[index] if (item.indexKey === activeNode.value.indexKey) isQuerySuccess = true if (item.refId) { const itemAssets = assetsMap[item.refId] if (itemAssets.layers) item.layers = await queryInfo(itemAssets.layers) else if (isQuerySuccess) { for (let index = 0; index < treeMeatData.value.assets; index++) { const assetsItem = treeMeatData.value.assets if (assetsItem.id === item.refId) break } treeMeatData.value.assets[index].p = image break } } else if (item.layers) { item.layers = await queryInfo(item.layers || []) } } } await queryInfo(treeMeatData.value.layers) return } }, { deep: true })
自此就完成动态编辑动效资源功能,就可以在任意位置进行动效素材编辑,完成动效的二次创作,进行快速的复用了。
图11 动态图片功能展示图片
3.3.3.5 核心功能 - 动态更新颜色
业务场景中有部分的动效是通过元素节点进行创建,这个时候就会存在修改元素节点的填充颜色的功能,下面我们就来看下如何实现该功能的。
颜色编辑功能:前置的步骤还是和资源编辑功能相同,主要的变化是在节点识别与配置表单生成逻辑上
首先需要识别当前选中节点的类型确定类型后进行初始化表单内容通过 indexKey 找到对应节点后,找到 path 元素,更新对应的颜色信息动态颜色编辑代码:
async function activeTreeNode(key: any, data: any) { const item = data.node.dataRef // 读取 全部配置颜色 item const result = await queryLayerFillColor(item) // 递归全部图层节点 找到元素类型的图层 获取到填充颜色 const uniqueMap = {} const uniqueResult = [] result.forEach((value: any) => { if (uniqueMap[value.join(',')]) return uniqueResult.push(value) uniqueMap[value.join(',')] = true }) editConfig.value = { uniqueColors: uniqueResult, allColors: result, ty: item.ty, } activeNode.value = item}watch( editConfig, async (value, oldValue) => { if (activeNode.value.ty === 0) { let colorIndex = 0 const setLayerFillColor = async (currentNode, colorList) => { const queryInfo = async (list: any) => { for (let index = 0; index < list.length; index++) { const item = list[index] if (item.indexKey === currentNode.indexKey) { if (!item.shapes) return await item.shapes.forEach(async sv => { sv.it.forEach(i => { if (i.ty !== 'fl') return i.c.k = colorList.map((cv, ci) => { return ci === 3 ? cv : cv / 256 }) }) }) } else if (item.layers) await queryInfo(item.layers || []) } } await queryInfo(treeMeatData.value.layers) } // 到达无图层节点后 进行查询填充颜色 const updateFillHandle = (item, index, node) => { // 填充颜色 item.it.forEach(i => { if (i.ty !== 'fl') return colorIndex = colorIndex + 1 const element = document.querySelectorAll(`.class_${node.indexKey} > g`) forEach(element, valueEle => { const childrenElement = valueEle.children[0] const childrenBgColor = allColors[index] childrenElement.setAttribute( 'fill', `rgb(${childrenBgColor[0]},${childrenBgColor[1]},${childrenBgColor[2]})` ) childrenElement.setAttribute('fill-opacity', childrenBgColor[3]) // 找到当前图层dom 进行修改fill if (childrenBgColor) setLayerFillColor(node, childrenBgColor) }) }) } // -> shapes / -> layers if (activeNode.value.shapes) { activeNode.value.shapes.forEach(item => { updateFillHandle(item, colorIndex, activeNode.value) }) } else if (activeNode.value.layers) { // 找到填充图层信息 const queryInfo = async (list: any) => { for (let index = 0; index < list.length; index++) { const item = list[index] if (item.refId) { const itemAssets = assetsMap[item.refId] if (itemAssets.layers) item.layers = await queryInfo(itemAssets.layers) } else if (item.layers) { item.layers = await queryInfo(item.layers || []) } if (item.shapes) { await item.shapes.forEach(async sv => { await updateFillHandle(sv, colorIndex, item) }) } } } queryInfo(activeNode.value.layers) } } }, { deep: true })
注意点:AE 导出的 Lottie 动效,颜色 rgba 值不是256进制,需要进行转换才能正常使用。
图12 动态颜色功能展示图片
通过以上的代码和功能已经完成平台的主要功能,平台动效单元的导入导出功能,由于篇幅受限就不展开讲解啦,感兴趣可以通过 Lottie 官网了解更新使用 Lottie 的小技巧。
图13 动效平台功能预览图
4 业务落地场景
我们都知道 Lottie 提供了一系列的 API 来控制动画,正是通过它我们才能实现各种复杂的业务场景,接下来我们可以看下如何通过该方式来支持复杂的业务动效。
4.1 营销域4.1.1 新年接好运
简单说明下场景,用户进入活动后可以看到好运红包雨,最终通过开启红包拿到奖励金额的交互动效。动效一共分为四个阶段:倒计时、好运雨、红包出现、红包开启。
可以看到其中的变量分别是:奖励金额、卡片图片、好运名称可以通过上述的资源编辑功能来完成不同的动效展示内容做过营销页的前端小伙伴都知道,这里通常会伴随着一个 ajax 请求去获取开启获得那个红包,大致流程是:
发起 ajax 请求,同时播放开启红包的动画ajax 请求返回后,播放对应的好运卡片与奖励金额,时长00:14
图14 新年接好运活动案例
4.1.2 HIMO艺术馆
业务场景是用户通过一段交互动画进入场馆观看HIMO集团的照片展览。该业务场景的动效共分为两个阶段:场馆出现、进入场馆。
该位置的动画交互需要用户点击按钮交互执行下一阶段的动画,让我们来看下如何使用Lottie来管控分段执行吧。
0~10 帧是场馆出现交互按钮11~199 帧是场馆进入动画,时长00:16
图15 海马体艺术馆案例
大致流程是:
先自动执行0~10帧动画,这个时候进行暂停,等待用户交互指定用户点击按钮 进行执行11~199帧动画,动画执行完成切换下一个场景可以通过图层的 cl 字段来获取到开启按钮的节点,在动效加载完成后绑定 click 事件,等该事件被触发后继续执行动画。
艺术馆处理代码:
const lottieAnimation = lottie.loadAnimation({ container: document.getElementById('guide-an'), renderer: 'svg', loop: false, autoplay: false, path: guideAn.value})lottieAnimation.addEventListener('enterFrame', event => { const currentFrame = event.currentTime if (currentFrame >= 10) lottieAnimation.pause()})lottieAnimation.addEventListener('DOMLoaded', () => { const btnJoin = document.querySelector('.btn-join') if (btnJoin) { btnJoin.addEventListener('click', debounce(1000, () => { lottieAnimation.play(11, true) })) }})
4.1.3 抽奖活动工具
业务场景是点击模块,触发「开始抽奖」的阶段,同步发布接口出去,接口返回后,将奖励通过弹窗的方式告知用户,交互场景比较简单,但是仔细分析还是发现不少问题。
0~109 帧「开始抽奖」110~180 帧「命中奖励」,时长00:07
图16 海马体抽奖活动案例
这里我们来分析下业务场景可能带来的问题:
「开始抽奖」点击模块 -> 接口响应很慢 -> 动效已完成。需要设计循环播放「开始抽奖」阶段,等待接口返回后,在关闭循环处理。通过 animation.loop = true 可以开启循环「开始抽奖」点击模块 -> 接口发生异常 -> 动效进行中。中断动画的执行,通过 animation.goToAndStop(0, true) 来中断,并且回到第0帧还需要注意的是,使用 playSegments,需要在 DOMLoaded 事件触发后执行,不然会出现播放完整动画后再执行片段动画的问题。这个是 lottie 目前存在的一个 bug抽奖动效交互处理代码:
// 初始化动效function initAnimation() { lottieAnimation.value = lottie.loadAnimation({ container: animationContainer.value, renderer: 'svg', loop: false, autoplay: false, path: props.animationConfig.animationFile }) proxy.$nextTick(() => { // 添加点击开始执行动画 openLotteryElement.addEventListener('click', handleAnimationClick) }) lottieAnimation.value.addEventListener('DOMLoaded', () => animationExecHandler('initExecuteFramesRange'))}// 动效执行处理函数async function animationExecHandler(type) { const animationActions = { initExecuteFramesRange: config => { // 初始执行帧范围 return config.initExecuteFramesRange.length === 0 ? [0, true] : config.initExecuteFramesRange }, openExecuteFramesRange: config => { // 开启执行帧范围 return config.openExecuteFramesRange.length === 0 ? [0, true] : config.openExecuteFramesRange }, endExecuteFramesRange: config => { // 结束帧执行范围 return config.endExecuteFramesRange.length > 0 ? config.endExecuteFramesRange : null }, reset: config => { // 重置处理 return config.initExecuteFramesRange.length === 0 ? [0, true] : [config.initExecuteFramesRange[1] - 1, config.initExecuteFramesRange[1]] } } const config = props.animationConfig const action = animationActions[type] if (!action) return const framesRange = action(config) if (framesRange) { const [frame, isStop] = framesRange if (isStop) { lottieAnimation.value.goToAndStop(frame, true) } else { await playAnimation(lottieAnimation.value, framesRange) } }}// 片段播放封装function playAnimation(animation, segements) { return new Promise(resolve => { animation.playSegments(segements, true) animation.addEventListener('complete', () => { resolve() }) })}
5 总结
文章最开始我们有讲到动效存在:实现成本高、沟通成本高和性能难以保证的问题。
但在以上的技术方案中,我们通过 Lottie 解决了动效接入的成本问题,在 Lottie 的基础上建立了动效平台,解决了动效沟通成本的问题,并且可以快速沉淀资产后快速复用,提高了动效的交互效率。通过采用 SVG 的渲染方式,我们相比使用 CSS3 实现方式,性能也得到较大的提升,已满足目前业务所需的目标。
动效平台方案最优秀的地方在于解决了研发成本的同时也解放了开发与设计,让伙伴可以投入到更有意义的工作当中去,不需要针对一个点进行反复沟通,最后得到一个质量不是很佳的动效。通过平台我们可以完成「设计产出动效」、「平台接入动效」、「开发伙伴渲染动画」三个环节的相互连接。
一路走来,从最开始的序列帧 webp 到支持小动画的 svga,再到现在能实现高交互的 Lottie 动效平台,发挥工程师的想法和解决问题的办法,一步步的提高动画模块的交付效率和降低研发投入的成本,让实现复杂动画不再是想象。
本文作者
高尔,来自缦图互联网中心前端团队。
来源-微信公众号:缦图coder
出处:https://mp.weixin.qq.com/s/4Qd5bhzbPWD3adGUrzkFlA