前言
公众号:【可乐前端】,期待关注交换,分享一些有意思的前端常识
在平时的开发过程中,前端或多或少都会遇到实现动画成果的场景。手写动画是一件相当麻烦的事件,调来调去不仅费时费力,可能还会被产品/UI吐槽:这动画成果也不难呀,为什么就不能实现呢?/为什么就没有还原成我想要的样子呢。
比如说产品让咱们实现这样的一个开关动效
明天咱们就用动画的实现形式——Lottie
,来百分百还原设计师的动画成果,并且能够大大提高咱们的工作效率(摸鱼工夫)。
Lottie
简介
首先咱们先来看一下,平时咱们实现动画都有哪些形式,它们别离有什么优缺点:
动画类型 | 长处 | 毛病 |
---|---|---|
CSS 动画 | 应用简便,通过@keyframes 和transition 创立动画;浏览器原生反对,性能较好 | 管制无限,不适用于简单动画;简单动画可能须要大量 CSS 代码,简短 |
JavaScript 动画 | 提供更高水平的管制和灵活性;实用于简单和精密动画成果 | 引入库减少页面累赘,可能须要学习曲线;使用不当容器对页面性能造成影响,产生卡顿 |
GIF 动画 | 制作和应用简略,无需额定代码;简直所有浏览器原生反对 | 无限色彩深度,不适用于所有场景;清晰度与文件尺寸成正比,无奈适应所有分辨率 |
Lottie | 反对矢量动画,放弃清晰度和流畅性 ;跨平台应用,实用于 iOS、Android 和 Web | 在一些较旧或性能较低的设施上,播放较大的 Lottie 动画可能会导致性能问题;对设计师要求较高 |
Lottie
是由Airbnb
开发的一个开源库,用于在挪动端和Web
上出现矢量动画。它基于JSON
格局的Bodymovin
文件,能够将由设计师在AE中创立的动画导出为可在Lottie库中播放的文件。
绝对于手写CSS/JS
动画而言,它能够大大减少前端开发的工作量,绝对于GIF
文件来说,它能够在一个正当的文件体积内保障动画的清晰度以及晦涩水平。上面咱们就介绍一下如何播放一个Lottie
动画,并实现一个炫酷的开关成果。
Hello Lottie
假如咱们当初曾经有一个Lottie
的json
文件,那么当初装置一些依赖
npm i react-lottie prop-types
装置完之后咱们就能够这样子来播放一个Lottie
动画:
import animationData from "../../assets/switch-lottie.json";const LottieSwitch = () => { const playing = useRef(false); const options = { loop: true, autoplay: true, animationData: animationData, rendererSettings: { preserveAspectRatio: "xMidYMid slice", }, }; return ( <Lottie options={options} height={20} width={40} /> );};
来解释一下下面的options
参数外面各个字段是什么意思:
loop
:是否循环播放autoplay
:是否自动播放animationData
:Lottie
动画json
资源rendererSettings.preserveAspectRatio
:指定如何在给定容器中渲染Lottie
动画xMidYMid
: 示意在程度和垂直方向上都在核心对齐- 示意放弃纵横比,但可能会裁剪超出容器的局部
正/反向播放
失常的把Lottie
动画播放进去之后,咱们就能够开始实现一个开关的性能。其实就是点击的时候更换Lottie
的播放方向,这里对应的是direction
字段,direction
为1
时正向播放,direction
为-1
时反向播放。
咱们就要实现上面的性能:
- 点击时切换方向
- 播放过程中加锁,禁止切换方向
- 监听播放完结事件,解锁
loop
改为false
;autoplay
改为false
实现代码如下:
const LottieSwitch = () => { const [direction, setDirection] = useState(null); const playing = useRef(false); const options = { loop: false, autoplay: false, animationData: animationData, rendererSettings: { preserveAspectRatio: "xMidYMid slice", }, }; const handleClick = () => { if (playing.current) { return; } playing.current = true; setDirection((prevState) => (prevState === 1 ? -1 : 1)); }; return ( <div style={{ padding: 40 }}> <div onClick={handleClick} className={styles.lottieWrapper}> <Lottie direction={direction} options={options} speed={2} height={20} width={40} eventListeners={[ { eventName: "complete", callback: () => { playing.current = false; }, }, ]} /> </div> </div> );};
这样咱们就是实现了一个开关的成果
继续时长
在Lottie
的json
中,有几个要害的字段跟动画的播放时长有关系:
fr
:帧率,每一秒的帧数ip
:开始帧op
:完结帧
如果说有上面的一个形容:
{ "fr": 30, "ip": 0, "op": 60,}
则示意帧率是30
帧,从第0
帧开始,60
帧完结,那这个动画的继续时长是 (op-ip)/fr
,为2s
。那如果咱们心愿整个动画的播放时长是500ms
,则只须要把Lottie
的倍速调整为4
。对应的就是speed
字段:
<Lottie direction={direction} options={options} speed={4} height={20} width={40} eventListeners={[ { eventName: "complete", callback: () => { playing.current = false; }, }, ]}/>
批改Lottie
在Lottie json
中,形容整个动画的过程以及成果其实对应的就是某个值。在实现的过程中,其实开发是能够去批改这些值的。比如说咱们能够批改下面开关的边框色彩以及小球的色彩。
首先在页面中找到小球对应的色彩是rgb(99, 102, 241)
在Lottie JSON
文件中,色彩信息通常呈现在示意图层款式的字段中。常见的字段是 "c"(color)
。"c"
字段示意色彩,通常以RGBA
格局(红绿蓝透明度)存储。例如:
"c": {"a":0,"k":[1,0,0,1]}
这示意红色,RGBA
值为 [1, 0, 0, 1]
。
rgb(99, 102, 241)
转成下面的写法那就是"c": {"a":0,"k":[99/255,102/255,241/255,1]}
。以99/255
为例,后果是0.38823529411764707
,那么就拿这个后果去json
文件中找到对应的节点。
对应有2
个后果,就是小球的色彩以及边框的色彩。当咱们找到这个值的时候,如果咱们想批改这个值,就必须晓得这个值的门路,在一个Lottie
中,想肉眼找到这个值的门路是一件很难的事件。所以咱们写一个辅助函数:
const updateJsonValue = (json, targetValue, newValue) => { const find = (json, targetValue, currentPath = []) => { for (const key in json) { if (json[key] === targetValue) { return [...currentPath, key]; } else if (typeof json[key] === "object" && json[key] !== null) { const path = find(json[key], targetValue, [...currentPath, key]); if (path) { return path; } } } }; const res = JSON.parse(JSON.stringify(json)); const path = find(res, targetValue); let current = res; for (let i = 0; i < path.length - 1; i++) { const key = path[i]; current = current[key]; } const lastKey = path[path.length - 1]; current[lastKey] = newValue; return json;};
下面的辅助函数就帮忙咱们找到这个值的门路,并批改目标值。比如说咱们想把目前的色彩改成绿色(rgb(25, 195, 125)
),就能够找到对应的门路,并批改。别忘了替换的时候把rgb
对应的值除以255
。
let newAnimationData = updateJsonValue(animationData, 0.388235300779, 0.09803921568627451)newAnimationData = updateJsonValue(newAnimationData, 0.388235300779, 0.09803921568627451)newAnimationData = updateJsonValue(newAnimationData, 0.40000000596, 0.7647058823529411)newAnimationData = updateJsonValue(newAnimationData, 0.40000000596, 0.7647058823529411)newAnimationData = updateJsonValue(newAnimationData, 0.945098042488, 0.49019607843137253)newAnimationData = updateJsonValue(newAnimationData, 0.945098042488, 0.49019607843137253)
把握了这种形式之后,咱们就能批改Lottie
外面的大部分内容,包含文案、资源图片、色彩等等。
最初
以上就是一些Lottie
的应用以及批改的介绍,下次再遇到比拟麻烦的动画需要。就能够跟产品说:能够做,让UI
给我导出一个Lottie
。
如果你有一些别的想法,欢送评论区交换~如果你感觉有意思的话,点点关注点点赞吧~