Skip to content

lottie

Lottie是一款由airbnb开源的跨平台动画渲染库,支持Android, iOS, Web, Windows平台。是专门用于解析从AE(Adobe After Effects)中通过 Bodymovin 插件导出的JSON文件,直接渲染动画。

属性与方法

优缺点

优点

  1. UI 同学通过 AE 制作动画,前端可以直接还原和控制,降低动画工作量;
  2. SVG 是可伸缩的,任何分辨率下都不会失真;
  3. JSON 文件大小会比 GIF 以及 APNG 等文件小很多,性能也会更好。

缺点:

  1. lottie-web 文件本身比较大,大小为 513KB,轻量版压缩后也有 144KB,经过 GZIP 后,大小为39k;
  2. 使用的 json 文件是由 AE 导出的,如果 UI 在设计的时候创建了很多图层,可能会导致 json 文件较大;
  3. 有部分 AE 动画的效果还不支持,lottie 无法实现或存在性能问题。

动画数据对象(json)结构

lottie-web 原理剖析

封装可复用 lottie 函数组件(React):

tsx
import React from 'react';
import lottie,  { type AnimationItem } from 'lottie-web';

type RendererType = 'svg' | 'canvas' | 'html';

interface LottieProps {
  // 是否循环播放
  loop?: boolean;
  // 渲染动画的类型
  renderer?: RendererType;
  // 是否自动播放
  autoplay?: boolean;
  // 动画渲染数据,与path互斥
  animationData?: any;
  // JSON文件路径,与animationData互斥
  path?: string;
  // 动画名称
  name?: string;
}

/**
 * 封装 lottie 函数组件
 */
const Lottie = React.forwardRef<
  {
    getInstance: () => AnimationItem | null;
    play: () => void;
    pause: () => void;
    stop: () => void;
  },
  LottieProps
>((props, ref) => {
  // 设置props的默认值
  const { loop = true, renderer = 'svg', path = '', animationData, autoplay = true, name = 'anim' } = props;
  const containerRef = React.useRef<HTMLDivElement | null>(null);
  const animationItemRef = React.useRef<AnimationItem | null>(null);
  // 暴露给从 props 传入的 ref 的方法
  React.useImperativeHandle(ref, () => ({
    // 获取当前动画对象实例
    getInstance: () => animationItemRef.current,
    // 播放,继续播放
    play: () => {
      animationItemRef.current?.play();
    },
    // 暂停动画
    pause: () => {
      animationItemRef.current?.pause();
    },
    // 停止动画,区别于暂停动画 pause()
    stop: () => {
      animationItemRef.current?.stop();
    },
  }));

  // 缓存 lottie 动画参数
  const animationParams = React.useMemo(
    () =>
      animationData ? { name, autoplay, loop, renderer, animationData } : { name, autoplay, loop, renderer, path },
    [loop, renderer, autoplay, path, animationData, name]
  );

  React.useEffect(() => {
    if (containerRef.current) {
      animationItemRef.current = lottie.loadAnimation({
        ...animationParams,
        container: containerRef.current,
      });
    }
    return () => {
      // 更新动画参数和卸载时,销毁动画对象,避免内存泄漏
      animationItemRef.current?.destroy();
      animationItemRef.current = null;
    };
  }, [animationParams]);
  return (
    // 渲染容器
    <div ref={containerRef} style={{ width: '100%', height: '100%' }}></div>
  );
});

export default Lottie;