前言

公众号:【可乐前端】,期待关注交换,分享一些有意思的前端常识

在平时的开发过程中,前端或多或少都会遇到实现动画成果的场景。手写动画是一件相当麻烦的事件,调来调去不仅费时费力,可能还会被产品/UI吐槽:这动画成果也不难呀,为什么就不能实现呢?/为什么就没有还原成我想要的样子呢。

比如说产品让咱们实现这样的一个开关动效

明天咱们就用动画的实现形式——Lottie,来百分百还原设计师的动画成果,并且能够大大提高咱们的工作效率(摸鱼工夫)。

Lottie简介

首先咱们先来看一下,平时咱们实现动画都有哪些形式,它们别离有什么优缺点:

动画类型长处毛病
CSS 动画应用简便,通过@keyframestransition创立动画;浏览器原生反对,性能较好管制无限,不适用于简单动画;简单动画可能须要大量 CSS 代码,简短
JavaScript 动画提供更高水平的管制和灵活性;实用于简单和精密动画成果引入库减少页面累赘,可能须要学习曲线;使用不当容器对页面性能造成影响,产生卡顿
GIF 动画制作和应用简略,无需额定代码;简直所有浏览器原生反对无限色彩深度,不适用于所有场景;清晰度与文件尺寸成正比,无奈适应所有分辨率
Lottie反对矢量动画,放弃清晰度和流畅性 ;跨平台应用,实用于 iOS、Android 和 Web在一些较旧或性能较低的设施上,播放较大的 Lottie 动画可能会导致性能问题;对设计师要求较高

Lottie是由Airbnb开发的一个开源库,用于在挪动端和Web上出现矢量动画。它基于JSON格局的Bodymovin文件,能够将由设计师在AE中创立的动画导出为可在Lottie库中播放的文件。

绝对于手写CSS/JS动画而言,它能够大大减少前端开发的工作量,绝对于GIF文件来说,它能够在一个正当的文件体积内保障动画的清晰度以及晦涩水平。上面咱们就介绍一下如何播放一个Lottie动画,并实现一个炫酷的开关成果。

Hello Lottie

假如咱们当初曾经有一个Lottiejson文件,那么当初装置一些依赖

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:是否自动播放
  • animationDataLottie动画json资源
  • rendererSettings.preserveAspectRatio:指定如何在给定容器中渲染Lottie动画

    • xMidYMid: 示意在程度和垂直方向上都在核心对齐
    • 示意放弃纵横比,但可能会裁剪超出容器的局部

正/反向播放

失常的把Lottie动画播放进去之后,咱们就能够开始实现一个开关的性能。其实就是点击的时候更换Lottie的播放方向,这里对应的是direction字段,direction1时正向播放,direction-1时反向播放。

咱们就要实现上面的性能:

  • 点击时切换方向
  • 播放过程中加锁,禁止切换方向
  • 监听播放完结事件,解锁
  • loop改为falseautoplay改为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>  );};

这样咱们就是实现了一个开关的成果

继续时长

Lottiejson中,有几个要害的字段跟动画的播放时长有关系:

  • 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

如果你有一些别的想法,欢送评论区交换~如果你感觉有意思的话,点点关注点点赞吧~