React: Lottie 动画初体验和优化策略

8,021 阅读5分钟

阅读大约需要 12-15 分钟, 主要简单介绍 lottie 动画概况和一些优化方案, 主要的优化方案针对React h5 的优化方案

1、什么是 LOTTIE

lottie 是 Aribnb 开源一个主要面向 Web、iOS、Android、React Native、Windows 的动画库,可以实时渲染After Effects动画,并以Bodymovin作为json导出,允许应用程序像使用静态图像一样轻松使用动画,一款协同合作的高效软件。

srS2vt.gif

简单来讲就是 UI 设计师用 AE 解析出 JSON 动画,前端工程师使用 JSON 文件在 lottie-web 等框架下进行 svg (canvas/html) 渲染

2、看几个简单的 DEMO

3、为什么选择 LOTTIE 动画,比较同样类型和选择

  • 同样类型的复杂动画实现的方案现在有如下几个

      1. png 序列帧:
      • 优点:兼容性好,工程师可控,操作性强
      • 缺点:它需要大量图片素材支持,动画播放时占用的内存较多
      1. 视频:
      • 优点:兼容性好,适配工作少
      • 缺点:交互弱,加载成本高
      1. gif:
      • 优点:实现简单。
      • 缺点:部分手机掉帧非常严重,体验不流畅,严重影响用户体验
  • lottie 动画就解决了这一问题:Lottie 只需要解析导出的 JSON 文件及所需要的图片,就能在各个平台上实现相同的动画效果,它实现成本低,上线后只需要动态替换对应的 JSON 文件就能实现可配置、可运营

4、简单的原理解析

来看看 bodymovin 动画的渲染基本流程

  • 1、 registerAnimation
    • 注册动画,创建一个AnimationItem的容器,把我们的节点elementanimationData json数据进行初始化
  • 2、 setData,setParams
    • animationData 设置基础值, 来确定数据来源并初始化数据,解析 svg/canvas/html 渲染方式
  • 3、 configAnimation
    • 挂载动画数据的参数
  • 4、 loadAnimation
    • load 动画
function loadAnimation(params){
    var animItem = new AnimationItem();
    setupAnimation(animItem, null);
    animItem.setParams(params);
    return animItem;
}
  • 其实lottie是用了 requestAnimationFrame----在于充分利用显示器的刷新机制,比较节省系统资源。

5、React 项目实现

  • 设计同学设计动画, AE 导出 zip 动画包,这里我们先使用 lottiefiles一下,然后可以根据情况来使用动画资源,一般是直接使用 json 文件即可;具体 React 项目如下,安装 `react-lottie
npm install react-lottie
  • 导入 json 文件
import Lottie from "lottie-react";
import groovyWalkAnimation from "./groovyWalk.json";

const Example = () => {
  return <Lottie animationData={groovyWalkAnimation} />;
};

export default Example;

实现比较简单

6、优化策略

  • 1、监控用户滑动事件,按页加载当前动画
  • 2、设计师侧优化(减少动画帧数,和动画数量)
  • 3、虚拟 DOM (react virtual dom)[swiperjs.com/api/#virtua…] 减少页面的渲染压力;
  • 4、可视范围监控 (可视范围 开启动画 inview 显示)[github.com/bitmap/reac…]
  • 5、根据手机性能,优雅降级

7、实践

  • 1、2 可以在大部分场景实现,这里只讨论上诉 3,4,5 的可行性

7.3、虚拟 dom

7.4、使用 inview

  • 可视范围的监控这里主要使用了 Intersection Observer API
    • 其中这个 api 最主要使用了 Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 viewport ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。
    • 解决了资源懒加载——当图片滚动到可见时才进行加载
import { useInView } from 'react-intersection-observer';
import Lottie from 'react-lottie';
import LOTTIE_ANIM_JSON from './lottie.json';

const Anim = ()=>{
  const { ref: $LOTTIE_ANIM_JSON_REF; View: $LOTTIE_ANIM_JSON_VIEW } = useInView();
  const $ANIM = (
    <Lottie
    options={{
      loop: true,
      autoplay: true,
      animationData: LOTTIE_ANIM_JSON,
    }}
    isPaused={!LOTTIE_ANIM_JSON_VIEW}
    />
    );

  return  <div className="lottie" ref={$LOTTIE_ANIM_JSON_REF}>
    {$ANIM}
  </div>
}

7.5、几种测算 web FPS 的方法

  • 7.5.1 requestAnimateFrame 基于 lottie 动画的原理特性充分的利用 requestAnimationframe 特性
window.requestAnimationFrame(callback);
  • requestAnimateFrame 想必前端同学在做一些 js 动画的时候已经比较了解了;告诉浏览器您希望执行动画并请求浏览器调用指定的函数在下一次重绘之前更新动画;

  • 具体的实现如下,测算浏览器页面渲染的动画 FPS

// 处理兼容性问题
var rAF = function () {
    return (
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        function (callback) {
            window.setTimeout(callback, 1000 / 60);
        }
    );
}();
 
var frame = 0;
var allFrameCount = 0;
var lastTime = Date.now();
var lastFameTime = Date.now();
 
var loop = function () {
    var now = Date.now();
    var fs = (now - lastFameTime);
    var fps = Math.round(1000 / fs);
 
    lastFameTime = now;
    // 不置 0,在动画的开头及结尾记录此值的差值算出 FPS
    allFrameCount++;
    frame++;
 
    if (now > 1000 + lastTime) {
        var fps = Math.round((frame * 1000) / (now - lastTime));
        console.log(`${new Date()} 1S内 FPS:`, fps); 
        frame = 0;
        lastTime = now;
    };
 
    rAF(loop);
}

loop();

下面看看掘金主页的加载速度

syePbQ.png

  • 值得注意的是,这个方法计算的结果和真实的帧率肯定是存在误差的,因为它是将每两次主线程执行 javascript 的时间间隔当成一帧,而非上面说的主线程加合成线程所消耗的时间为一帧。但是对于现阶段而言,算是一种可取的方法。

  • 7.5.2 chrome 开发者工具

    • 控制台-> perfmance -> 刷新页面开启监控性能,然后就能看到 frames 这项性能指标(右上角有 fps 的帧率控制) syngHg.jpg
  • 7.5.3 Frame API

    • 什么是 Frame Timing API ? Frame Timing API 是 Web Performance Timing API 标准中的其中一位成员。

    • Web Performance Timing API 是 W3C 推出的一套性能 API 标准,用于帮助开发者对网站各方面的性能进行精确的分析与控制,提升 Web 网站性能。

var rendererEvents = window.performance.getEntriesByType("renderer");

// 下面可以看到 render 的时间
{
  sourceFrameNumber: 120,
  startTime: 1342.549374253
  cpuTime: 6.454313323
}

syV0yD.png

但是这个 api 还在试验阶段,各大浏览器都不支持所以只是暂时讨论和了解一下就可以了,不能正式的开发使用;

参考

看完最后打个小广告

蚂蚁金服,RichLab(支付宝花呗借呗团队) 招人啦,p5-p8 都要,应届生可以,
base:重庆,杭州,北京;
毕竟我是西南张家辉,优先推重庆滴,有四川和重庆人,或者想感受重庆风情的同学欢迎来撩;
绿色软件:Lpovexjn