lottie

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Lottie Animation Guidelines

Lottie动画实施指南

You are an expert in Lottie animations, web performance, and JavaScript. Follow these guidelines when implementing Lottie animations.
您是Lottie动画、网页性能和JavaScript方面的专家。在实现Lottie动画时,请遵循以下指南。

Core Principles

核心原则

Use dotLottie Format

使用dotLottie格式

  • Prefer
    .lottie
    (dotLottie) format over
    .json
    - up to 90% smaller file size
  • dotLottie bundles all assets (images, fonts) into a single compressed file
  • Use the free dotLottie converter at lottiefiles.com
  • 优先使用
    .lottie
    (dotLottie)格式而非
    .json
    格式 - 文件大小最多可减小90%
  • dotLottie将所有资源(图片、字体)打包到单个压缩文件中
  • 使用lottiefiles.com上的免费dotLottie转换器

Installation

安装

bash
undefined
bash
undefined

For React

For React

npm install @lottiefiles/dotlottie-react
npm install @lottiefiles/dotlottie-react

For vanilla JS

For vanilla JS

npm install @lottiefiles/dotlottie-web
undefined
npm install @lottiefiles/dotlottie-web
undefined

React Implementation

React实现

Basic Usage

基础用法

tsx
import { DotLottieReact } from "@lottiefiles/dotlottie-react";

function Animation() {
  return (
    <DotLottieReact
      src="/animations/loading.lottie"
      loop
      autoplay
    />
  );
}
tsx
import { DotLottieReact } from "@lottiefiles/dotlottie-react";

function Animation() {
  return (
    <DotLottieReact
      src="/animations/loading.lottie"
      loop
      autoplay
    />
  );
}

Control Animation Playback

控制动画播放

tsx
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import { useState } from "react";

function ControlledAnimation() {
  const [dotLottie, setDotLottie] = useState(null);

  const dotLottieRefCallback = (dotLottie) => {
    setDotLottie(dotLottie);
  };

  return (
    <>
      <DotLottieReact
        src="/animation.lottie"
        dotLottieRefCallback={dotLottieRefCallback}
      />
      <button onClick={() => dotLottie?.play()}>Play</button>
      <button onClick={() => dotLottie?.pause()}>Pause</button>
      <button onClick={() => dotLottie?.stop()}>Stop</button>
    </>
  );
}
tsx
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import { useState } from "react";

function ControlledAnimation() {
  const [dotLottie, setDotLottie] = useState(null);

  const dotLottieRefCallback = (dotLottie) => {
    setDotLottie(dotLottie);
  };

  return (
    <>
      <DotLottieReact
        src="/animation.lottie"
        dotLottieRefCallback={dotLottieRefCallback}
      />
      <button onClick={() => dotLottie?.play()}>Play</button>
      <button onClick={() => dotLottie?.pause()}>Pause</button>
      <button onClick={() => dotLottie?.stop()}>Stop</button>
    </>
  );
}

Performance Optimization

性能优化

Lazy Loading

懒加载

tsx
import { useEffect, useRef, useState } from "react";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";

function LazyLottie({ src }) {
  const [isVisible, setIsVisible] = useState(false);
  const containerRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { rootMargin: "100px" }
    );

    if (containerRef.current) {
      observer.observe(containerRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef}>
      {isVisible && <DotLottieReact src={src} autoplay loop />}
    </div>
  );
}
tsx
import { useEffect, useRef, useState } from "react";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";

function LazyLottie({ src }) {
  const [isVisible, setIsVisible] = useState(false);
  const containerRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { rootMargin: "100px" }
    );

    if (containerRef.current) {
      observer.observe(containerRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef}>
      {isVisible && <DotLottieReact src={src} autoplay loop />}
    </div>
  );
}

Choose the Right Renderer

选择合适的渲染器

tsx
// SVG renderer - best quality, good for simple animations
<DotLottieReact src="/animation.lottie" renderer="svg" />

// Canvas renderer - better performance for complex animations
<DotLottieReact src="/animation.lottie" renderer="canvas" />

// Use canvas for:
// - Complex animations with many elements
// - Lower-powered devices
// - Animations with filters/effects
tsx
// SVG渲染器 - 画质最佳,适合简单动画
<DotLottieReact src="/animation.lottie" renderer="svg" />

// Canvas渲染器 - 复杂动画性能更优
<DotLottieReact src="/animation.lottie" renderer="canvas" />

// 使用canvas的场景:
// - 包含大量元素的复杂动画
// - 性能较低的设备
// - 带有滤镜/特效的动画

Reduce DOM Elements

减少DOM元素

  • Reuse identical graphic elements in After Effects
  • Simplify paths and reduce keyframes
  • Avoid unnecessary layers
  • Target under 1000 DOM elements per animation
  • 在After Effects中复用相同的图形元素
  • 简化路径并减少关键帧
  • 避免不必要的图层
  • 每个动画的DOM元素数量控制在1000个以内

Animation Design Best Practices

动画设计最佳实践

Avoid Performance-Heavy Features

避免使用性能消耗大的特性

AVOID:
- Masks (use alpha matte sparingly)
- Complex blur effects
- 3D layers
- Expressions
- Uncompressed images
- Large image assets

PREFER:
- Simple shapes (fills, strokes)
- Transform animations (position, scale, rotation)
- Opacity changes
- Path animations
避免:
- 遮罩(尽量少用alpha遮罩)
- 复杂模糊效果
- 3D图层
- 表达式
- 未压缩图片
- 大尺寸图片资源

推荐:
- 简单形状(填充、描边)
- 变换动画(位置、缩放、旋转)
- 透明度变化
- 路径动画

Optimize Images in Animations

优化动画中的图片

- Compress images to match display size
- If max display is 400x400, don't use 1000x1000 images
- Use vector graphics when possible
- Consider converting images to shapes
- 根据显示尺寸压缩图片
- 如果最大显示尺寸为400x400,不要使用1000x1000的图片
- 尽可能使用矢量图形
- 考虑将图片转换为形状

Interactivity

交互性

Cursor/Mouse Interaction

光标/鼠标交互

tsx
<DotLottieReact
  src="/hover-animation.lottie"
  playMode="hover"
/>
tsx
<DotLottieReact
  src="/hover-animation.lottie"
  playMode="hover"
/>

Scroll-Linked Animation

滚动联动动画

tsx
import { useScroll, useTransform } from "motion/react";

function ScrollLottie() {
  const { scrollYProgress } = useScroll();
  const [dotLottie, setDotLottie] = useState(null);

  useEffect(() => {
    if (!dotLottie) return;

    const unsubscribe = scrollYProgress.on("change", (progress) => {
      dotLottie.setFrame(progress * dotLottie.totalFrames);
    });

    return unsubscribe;
  }, [dotLottie, scrollYProgress]);

  return (
    <DotLottieReact
      src="/scroll-animation.lottie"
      dotLottieRefCallback={setDotLottie}
      autoplay={false}
    />
  );
}
tsx
import { useScroll, useTransform } from "motion/react";

function ScrollLottie() {
  const { scrollYProgress } = useScroll();
  const [dotLottie, setDotLottie] = useState(null);

  useEffect(() => {
    if (!dotLottie) return;

    const unsubscribe = scrollYProgress.on("change", (progress) => {
      dotLottie.setFrame(progress * dotLottie.totalFrames);
    });

    return unsubscribe;
  }, [dotLottie, scrollYProgress]);

  return (
    <DotLottieReact
      src="/scroll-animation.lottie"
      dotLottieRefCallback={setDotLottie}
      autoplay={false}
    />
  );
}

Segment Playback

分段播放

tsx
function SegmentAnimation() {
  const [dotLottie, setDotLottie] = useState(null);

  const playSegment = (start, end) => {
    dotLottie?.setSegment(start, end);
    dotLottie?.play();
  };

  return (
    <>
      <DotLottieReact
        src="/multi-state.lottie"
        dotLottieRefCallback={setDotLottie}
        autoplay={false}
      />
      <button onClick={() => playSegment(0, 30)}>State 1</button>
      <button onClick={() => playSegment(30, 60)}>State 2</button>
    </>
  );
}
tsx
function SegmentAnimation() {
  const [dotLottie, setDotLottie] = useState(null);

  const playSegment = (start, end) => {
    dotLottie?.setSegment(start, end);
    dotLottie?.play();
  };

  return (
    <>
      <DotLottieReact
        src="/multi-state.lottie"
        dotLottieRefCallback={setDotLottie}
        autoplay={false}
      />
      <button onClick={() => playSegment(0, 30)}>State 1</button>
      <button onClick={() => playSegment(30, 60)}>State 2</button>
    </>
  );
}

Accessibility

无障碍适配

Respect Reduced Motion

尊重减少动画偏好

tsx
function AccessibleAnimation() {
  const prefersReducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;

  if (prefersReducedMotion) {
    return <img src="/static-fallback.svg" alt="Animation description" />;
  }

  return (
    <DotLottieReact
      src="/animation.lottie"
      autoplay
      loop
      aria-label="Decorative loading animation"
    />
  );
}
tsx
function AccessibleAnimation() {
  const prefersReducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;

  if (prefersReducedMotion) {
    return <img src="/static-fallback.svg" alt="Animation description" />;
  }

  return (
    <DotLottieReact
      src="/animation.lottie"
      autoplay
      loop
      aria-label="Decorative loading animation"
    />
  );
}

Provide Fallbacks

提供降级方案

tsx
function AnimationWithFallback() {
  const [hasError, setHasError] = useState(false);

  if (hasError) {
    return <img src="/fallback.gif" alt="Animation" />;
  }

  return (
    <DotLottieReact
      src="/animation.lottie"
      autoplay
      onError={() => setHasError(true)}
    />
  );
}
tsx
function AnimationWithFallback() {
  const [hasError, setHasError] = useState(false);

  if (hasError) {
    return <img src="/fallback.gif" alt="Animation" />;
  }

  return (
    <DotLottieReact
      src="/animation.lottie"
      autoplay
      onError={() => setHasError(true)}
    />
  );
}

Loading Strategy

加载策略

Use Preloader for Large Animations

为大型动画使用预加载器

tsx
function AnimationWithPreloader() {
  const [isLoaded, setIsLoaded] = useState(false);

  return (
    <div className="animation-container">
      {!isLoaded && (
        <img src="/first-frame.webp" alt="" className="preloader" />
      )}
      <DotLottieReact
        src="/large-animation.lottie"
        onLoad={() => setIsLoaded(true)}
        style={{ opacity: isLoaded ? 1 : 0 }}
        autoplay
      />
    </div>
  );
}
tsx
function AnimationWithPreloader() {
  const [isLoaded, setIsLoaded] = useState(false);

  return (
    <div className="animation-container">
      {!isLoaded && (
        <img src="/first-frame.webp" alt="" className="preloader" />
      )}
      <DotLottieReact
        src="/large-animation.lottie"
        onLoad={() => setIsLoaded(true)}
        style={{ opacity: isLoaded ? 1 : 0 }}
        autoplay
      />
    </div>
  );
}

File Size Guidelines

文件大小指南

Animation ComplexityTarget SizeMax DOM Elements
Simple icons< 10KB< 100
UI animations< 50KB< 500
Complex scenes< 150KB< 1500
Hero animations< 300KB< 2500
动画复杂度目标大小最大DOM元素数量
简单图标< 10KB< 100
UI动画< 50KB< 500
复杂场景< 150KB< 1500
首页横幅动画< 300KB< 2500

Cleanup

资源清理

Proper Cleanup in React

React中的正确清理

tsx
useEffect(() => {
  return () => {
    dotLottie?.destroy();
  };
}, [dotLottie]);
tsx
useEffect(() => {
  return () => {
    dotLottie?.destroy();
  };
}, [dotLottie]);

Best Practices Summary

最佳实践总结

  1. Use dotLottie format for smaller file sizes
  2. Lazy load animations not in viewport
  3. Use canvas renderer for complex animations
  4. Avoid masks, blurs, and expressions
  5. Compress and optimize image assets
  6. Respect reduced motion preferences
  7. Provide static fallbacks for errors
  8. Clean up animations on unmount
  9. Keep DOM element count low
  10. Use preloaders for large animations
  1. 使用dotLottie格式减小文件大小
  2. 对不在视口中的动画使用懒加载
  3. 对复杂动画使用canvas渲染器
  4. 避免使用遮罩、模糊和表达式
  5. 压缩并优化图片资源
  6. 尊重减少动画的用户偏好
  7. 为错误情况提供静态降级方案
  8. 在组件卸载时清理动画资源
  9. 控制DOM元素数量在较低水平
  10. 为大型动画使用预加载器