🌟前端使用Lottie实现炫酷的开关效果🌟

前言

在平时的开发过程中,前端或多或少都会遇到实现动画效果的场景。手写动画是一件相当麻烦的事情,调来调去不仅费时费力,可能还会被产品/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文件,那么现在安装一些依赖

css 复制代码
npm i react-lottie prop-types

安装完之后我们就可以这样子来播放一个Lottie动画:

jsx 复制代码
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

实现代码如下:

js 复制代码
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:结束帧

假如说有下面的一个描述:

json 复制代码
{
  "fr": 30,
  "ip": 0,
  "op": 60,
}

则表示帧率是30帧,从第0帧开始,60帧结束,那这个动画的持续时长是 (op-ip)/fr,为2s。那如果我们希望整个动画的播放时长是500ms,则只需要把Lottie的倍速调整为4。对应的就是speed字段:

ini 复制代码
<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格式(红绿蓝透明度)存储。例如:

css 复制代码
"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中,想肉眼找到这个值的路径是一件很难的事情。所以我们写一个辅助函数:

js 复制代码
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

ini 复制代码
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

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

相关推荐
weixin_382395239 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__9 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.9 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~10 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华11 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子13 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅13 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni14 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学15 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端