前端啊,拿Lottie炫个动画各17

10,811 阅读11分钟

点赞 + 关注 + 收藏 = 学会亄17/strong>

本文箄17仄17/h2>

有时候在网页上看到一些很炫酷的小动画,比如loading特效,还能控制这个动画的状17,真的觉得很神奇1717/p>

大部分做后端的不想碰前端,做前端的不想碰动画特效〄17/p>

其实啊,很多时17不霄17要自己写炫酷的特效,会调用第三方库已经挺厉害的了。比如今天要介绍的17 Lottie〄17/p>

01.gif

Lottie 是什么?

🔗Lottie官网 airbnb.io/lottie/

Lottie 是一个17用亄17 Android、iOS、Web 咄17 Windows 的库,它可以解析使用 Bodymovin 导出丄17 JSON 的17 Adobe After Effects 动画,并在移动设备和 Web 上本地渲染它们!

After Effects 是什么?Bodymovin 又是仄17么?

别17,这些我也不会。作为前端,我会拿别人做好的东西来用😁

箄17单来说,Lottie 昄17 Airbnb 弄17发的动画库,特别适合前端弄17发人员17它可以轻松实现复杂的动画效果,不需要手写大量代码,只需引入现成的17 JSON 文件即可〄17/p>

今天不讲iOS,不讲Android,只讲如何在前端使用 Lottie〄17/p>

安装 Lottie Web

要在前端项目中使甄17 Lottie,要么用 CDN 的方式引入,要么通过 NPM 下载〄17/p>

CDN

在这个网坄17可以找到 Lottie 的各个版本的JS文件$17 cdnjs.com/libraries/b 17/a>

02.png

我使用的昄17 5.12.2 这个版本

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #lottie {
      width: 200px;
      height: 200px;
    }
  </style>
</head>
<body>
  <div id="lottie"></div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js"></script>
  <script>
    var animation = lottie.loadAnimation({
      container: document.getElementById('lottie'), // 渲染动画的容噄17/span>
      renderer: 'svg', // 渲染方式
      loop: true, // 是否循环
      autoplay: true, // 是否自动播放
      path: './Animation_1.json' // 动画 JSON 文件的路径17/span>
    });
  </script>
</body>
</html>

Animation_1.json 是我下载的一个动画文件,这个文件我放在同级目录里。这个动画文件在哪可以下载我接下来会介绍。这里先了解丄17丄17 CDN 的方式17么引入 Lottie 即可〄17/p>

NPM

用下面这个命令将 Lottie 下载到你的项目里〄17/p>

npm install lottie-web

动画资源下载

前面介绍到,动画是用 AE 做好,然后用 Bodymovin 插件将动画转换成丄17丄17 JSON 文件,前端就可以使用 lottie-web 将这丄17 JSON 文件的内容转换成图像渲染到浏览器页面上1717/p>

如果想要现成的动画资源可以在这些地方找找

我这里也给大家准备了丄17个动画文件,大家可以拿它来练手1717/p>

实现第一丄17 Lottie 动画

我17过 React 脚手架创建了丄17丄17 React 项目来举例说明如何使甄17 Lottie,在 Vue 里的用法也是丄17样的〄17/p>

03.gif

import React, { useEffect, useRef } from 'react';
import lottie from 'lottie-web';
import animationData from './assets/animations/Animation.json';

function App() {
  const containerRef = useRef(null);

  useEffect(() => {
    const anim = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData: animationData
    });
  }, []);

  return <div ref={containerRef} style={{width: "300px", height: "300px"}}></div>;
}

export default App;

圄17 HTML 文件中,创建丄17个容器,用于放置 Lottie 动画。在这个例子中我创建了一个宽和高都是 300px 的17 div 元素〄17/p>

然后引入 lottie-web 以及放在前端项目里的 Animation.json 动画文件〄17/p>

朄17后调甄17 lottie.loadAnimation() 来启动动画17它将一个对象作为唯丄17参数〄17/p>

  • container:动画容器,这个例子通过 React 提供的语法获取到 DOM 元素〄17/li>
  • renderer:渲染方式,可1717 svg〄17code>canvas 咄17 html〄17/li>
  • loop:是否循环播放1717/li>
  • autoplay:是否自动播放1717/li>
  • animationData:本地的动画数据的对象1717/li>

这里霄17要注意,animationData 接收的动画对象是存放在前端项目的 JSON 文件,如果你的动画文件是存在别的服务器,霄17要17过丄17丄17 URL 引入的话就不能用 animationData 来接收了,17是要改戄17 path〄17/p>

const anim = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      path: 'https://lottie.host/68bd36a3-b21d-4909-9b61-9be6b0947943/gInO8owFG1.json'
    });

Lottie 常用功能

播放、暂停17停歄17/h3>

控制动画的播放17暂停17停止是很常用的功能〄17/p>

  • 播放:使甄17 play() 方法。顾名17义就是让动画动起来〄17/li>
  • 暂停:使甄17 pause()方法。暂停可以让动画在当前帧停下来17可以这么理解,你在看视频一丄17秒的短视频,播放到第7秒的时17你按了“暂停17,画面就停在第7秒的地方了1717/li>
  • 停止:使甄17 stop() 方法。停止和暂停都是让动画停下来,17停止会让动画返回第1帧画面的地方停下来1717/li>

04.gif

import lottie from 'lottie-web';
import React, { useEffect, useRef } from 'react';
import animationData from './assets/animations/Animation.json';

function App() {
  const containerRef = useRef(null);

  let anim = null

  useEffect(() => {

    anim = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData: animationData,
    });
  }, []);

	// 播放动画
  function play() {
    anim.play()
  }

  // 暂停动画
  function pause() {
    anim.pause()
  }

  // 停止动画
  function stop() {
    anim.stop()
  }

  return <>
    <div ref={containerRef} style={{width: "300px", height: "300px"}}></div>
    <button onClick={play}>播放</button>
    <button onClick={pause}>暂停</button>
    <button onClick={stop}>停止</button>
  </>;
}

export default App;

代码放这,建议自己运行起来体验一下1717/p>

设置动画播放速度

使用 setSpeed() 方法可以设置动画的播放17度,传入一个数字即可17默认的播放速度昄1717/p>

05.gif

// 省略部分代码

// 2倍17度播放
anim.setSpeed(2)

这个参数支持正数(包括非整数)1717负数1717/p>

  • 大于1的正数:比默认17度忄17/li>
  • 大于0小于1:比默认速度慄17/li>
  • 0:画面停止在第一帧不动了
  • 小于0大于-1:动画17放,17且速度比默认17慢
  • -1:动画17放,17度和默认17一栄17/li>
  • 小于-1:动画17放,17度比默认17快

设置动画播放方向

这里说的播放方向指的是17正睄17放17还是1717着放1717前面用 setSpeed() 方法可以做到这点。但还有丄17个叫 setDirection() 的方法也能做到1717/p>

setDirection() 接收丄17个数字参数,这个参数大于等于0时是正着播放,负数时是17着播放。17常情况下,想17着播放会传兄17 -1〄17/p>

06.gif

// 省略部分代码

anim.setDirection(-1)

看,面是吐出来的〄17/p>

设置动画进度

通过 goToAndStop() 方法可以控制动画跳转到指定帧或时间并停止〄17/p>

goToAndStop(value, isFrame) 接收2个参数1717/p>

  • value:数值,表示要跳转到的帧数或时间点1717/li>
  • isFrame:布尔17,默认丄17 false。如果设置为 true,则 value 参数表示帧数;如果设置为 false,则 value 参数表示时间(以毫秒为单位)〄17/li>

07.png

function goToAndStop() {
  anim.goToAndStop(1000, false)
}

return <>
    <div ref={containerRef} style={{width: "300px", height: "300px"}}></div>
    <button onClick={goToAndStop}>跳转刄1717span class="hljs-tag"></button>
</>;

如果 goToAndStop 第二个参数为 true 则表示要跳转到指定帧数,这个值不能超过动画的总帧数1717/p>

锄17毁动画实侄17/h3>

有些场景在某个时刻需要将动画元素删除掉,比如在数据加载时霄17要显礄17 loading,数据加载成功或者失败后霄17要隐藄17 loading,此时可以用 destroy 射17 Lottie 动画实例锄17毁掉〄17/p>

// 省略部分代码

anim.destroy()

动画监听事件

动画有很多个状17,比如动画数据加载完成/失败、动画播放结束17循环下丄17次播放17进入新的一帧17Lottie 为我们提供了几个常用的监听方法1717/p>

而要监听这些事件,需要在 lottie 实例上用 addEventListener 方法绑定各个事件〄17/p>

动画数据加载情况

监听动画数据(JSON文件)加载成功或者失败,可以用这两个方法〄17/p>

  • data_ready:数据加载成功后执行〄17/li>
  • data_failed:数据加载失败后执行〄17/li>

霄17要注意,这两个方法只适用 path 的方式加载数据时触发〄17code>animationData 加载的是本地数据,并不会触发这两个方法1717/p>

// 省略部分代码

let anim = null;

useEffect(() => {
  anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    path: 'https://lottie.host/68bd36a3-b21d-4909-9b61-9be6b0947943/gInO8owFG1.json'
  });

  anim.addEventListener('data_ready', () => {
    console.log('数据加载完成');
  });

  anim.addEventListener('data_failed', () => {
    console.log('数据加载失败');
  })
}, []);

初始配置完成各17/h4>

在数据加载前,还可以通过 config_ready 监听初始化配置的完成情况〄17/p>

要让 config_ready 生效,同样需要17过 path 的方式加载数据1717/p>

config_ready 的执行顺序排圄17 data_ready 之前〄17/p>

// 省略部分代码

let anim = null;

useEffect(() => {
  anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    path: 'https://lottie.host/68bd36a3-b21d-4909-9b61-9be6b0947943/gInO8owFG1.json'
  });

  anim.addEventListener('data_ready', () => {
    console.log('数据加载完成');
  });
  
  anim.addEventListener('config_ready', () => {
    console.log('初始化成劄17');
  });
}, []);

动画播放结束

当动画播放结束时,会触发 complete 事件〄17/p>

如果 loop 丄17 true 的话时不会触叄17 complete 的,因为丄17直循环的话动画是没有结束的那天1717/p>

// 省略部分代码

let anim = null;

useEffect(() => {

  anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: false,
    autoplay: true,
    animationData: animationData,
  });

  anim.addEventListener('complete', () => {
    console.log('动画播完亄17');
  });
}, []);

动画循环播放结束

彄17 loop 丄17 true 时,每循环播放完丄17次就会触叄17 loopComplete 事件〄17/p>

// 省略部分代码

let anim = null;

useEffect(() => {

  anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData: animationData,
  });

  anim.addEventListener('loopComplete', () => {
    console.log('循环结束,准备进入下丄17次循玄17');
  });
}, []);

当你通过 pause() 暂停了动画,过一阵用 play() 继续播放,也会等这次动画完整播放完才会触叄17 loopComplete〄17/p>

进入新的丄17帄17/h4>

丄17个动画由很多个画面组成,每个画面都属亄1717动画每进入丄17帧时都会触发 enterFrame 事件〄17/p>

// 省略部分代码

let anim = null;

useEffect(() => {

  anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData: animationData,
    // path: 'https://lottie.host/68bd36a3-b21d-4909-9b61-9be6b0947943/gInO8owFG1.json'
  });

  anim.addEventListener('enterFrame', () => {
    console.log('进入新帧');
  });
}, []);

自己手写丄17个动画JSON$17/h2>

手写 Lottie 的17 JSON 动画文件相对复杂,因为需要对 Lottie 的17 JSON 结构有较深入的理解17Lottie 的17 JSON 文件基于 Bodymovin 插件输出的格式,主要包含静17资源17图层17形状以及帧动画信息〄17/p>

由于相对复杂,所以不建议真的自己手写,这会显得你很傻〄17/p>

Lottie JSON 文件由多个部分组成,主要包括$17/p>

  1. assets:动画中使用的资源(图片等)〄17/li>
  2. layers:动画中的每丄17层(类似亄17 Photoshop 图层)1717/li>
  3. shapes:定义图形17路径等基本元素及其动画〄17/li>
  4. animations:定义每丄17帧的动画数据,包括位置17缩放1717明度等〄17/li>

太复杂的元素我确实手写不出来,只能写丄17个简单的圆形从左向右移动演示丄17下1717/p>

08.gif

{
  "v": "5.6.10",           // Lottie 版本
  "fr": 30,                // 帧率 (Frames per second)
  "ip": 0,                 // 动画弄17始帧 (In Point)
  "op": 60,                // 动画结束帄17 (Out Point)
  "w": 500,                // 画布宽度
  "h": 500,                // 画布高度
  "nm": "circle animation",// 动画名称
  "ddd": 0,                // 是否昄17 3D 动画
  "assets": [],            // 静17资源(如图片等$17/span>
  "layers": [              // 动画的图屄17/span>
    {
      "ddd": 0,            // 图层是否昄17 3D
      "ind": 1,            // 图层索引
      "ty": 4,             // 图层类型$17代表形状图层
      "nm": "circle",      // 图层名称
      "sr": 1,             // 图层的播放17度
      "ks": {              // 图层的关键帧属17(动画数据$17/span>
        "o": {             // 不17明度动甄17/span>
          "a": 0,          // 不17明度动画为 0,表示不设置动画
          "k": 100         // 不17明度固定为 100%
        },
        "r": {             // 旋转动画
          "a": 0,          // 不设置动甄17/span>
          "k": 0           // 旋转角度丄17 0
        },
        "p": {             // 位置动画 (Position)
          "a": 1,          // a 丄17 1 表示位置有动甄17/span>
          "k": [
            {
              "i": { "x": 0.667, "y": 1 },  // 起始位置插1717/span>
              "o": { "x": 0.333, "y": 0 },  // 终止位置插1717/span>
              "n": "0p667_1_0p333_0",       // 插17模式名秄17/span>
              "t": 0,                       // 起始帄17/span>
              "s": [50, 250, 0],            // 起始位置 (x: 50, y: 250)
              "e": [450, 250, 0],           // 结束位置 (x: 450, y: 250)
              "to": [66.66667, 0, 0],       // 起始插17控制点
              "ti": [-66.66667, 0, 0]       // 终止插17控制点
            },
            { "t": 60 }                     // 圄17 60 帧时结束动画
          ]
        },
        "a": {             // 锚点动画(用于旋转或缩放中心$17/span>
          "a": 0,
          "k": [0, 0, 0]   // 锚点固定圄17 (0, 0)
        },
        "s": {             // 缩放动画 (Scale)
          "a": 0,
          "k": [100, 100, 100]  // 保持 100% 缩放
        }
      },
      "ao": 0,             // 自动定向
      "shapes": [          // 图形数组,定义图层中的形犄17/span>
        {
          "ty": "el",      // 图形类型 'el' 代表 ellipse(椭圄17/圆形$17/span>
          "p": {           // 椭圆的中心点
            "a": 0,
            "k": [0, 0]
          },
          "s": {           // 椭圆的大射17/span>
            "a": 0,
            "k": [100, 100]  // 圆的宽和高为 100px
          },
          "nm": "ellipse"
        },
        {
          "ty": "st",      // 图形类型 'st' 代表 stroke(描边)
          "c": {           // 描边颜色
            "a": 0,
            "k": [1, 0, 0, 1]  // 红色 [R: 1, G: 0, B: 0, Alpha: 1]
          },
          "o": {           // 描边不17明庄17/span>
            "a": 0,
            "k": 100
          },
          "w": {           // 描边宽度
            "a": 0,
            "k": 10
          },
          "lc": 1,         // 线帽样式
          "lj": 1,         // 线接样式
          "ml": 4          // 折线限制
        }
      ],
      "ip": 0,             // 图层弄17始帧
      "op": 60,            // 图层结束帄17/span>
      "st": 0,             // 图层起始时间
      "bm": 0              // 混合模式
    }
  ]
}
  • v: 表示 Lottie 动画的版本1717/p>

  • fr: 帧率,表示每秒多少帧。在这个示例中,每秒播放 30 帧1717/p>

  • ip 咄17 op: 分别代表动画的起始帧和结束帧。本例中,动画从笄17 0 帧开始,到第 60 帧结束1717/p>

  • layers: 图层数组。每个图层包各17 ks (关键帧属怄17),用于控制位置17缩放17旋转等动画参数〄17/p>

    • ty: 4: 图层类型为形状图层1717/li>
    • p: 定义了位置动画,从帧 0 弄17始,圆形仄17 (50, 250) 移动刄17 (450, 250) 的位置,表示从画布左侧移动到右侧〄17/li>
  • shapes: 定义了图形的属171717/p>

    • el: 表示丄17个椭圆形,即我们定义的圆形1717/p>

    • st: 表示圆形的描边,颜色为红色,宽度丄17 10px〄17/p>


以上就是本文的全部内容,如果本文对你有帮助,欢迎转发给你的朋友1717/p>

IMG_4387 2.GIF

点赞 + 关注 + 收藏 = 学会亄17/strong>