Lottie-web 源码解析(一):从 JSON Schema 认识 Lottie 动画的本质📒

Lottie 的本质是 After Effects 动画的 Web 播放器,其工作流程清晰简洁:

  1. 设计 → 设计师在 After Effects(AE) 中制作矢量动画
  2. 导出 → 通过 Bodymovin 插件将动画导出为轻量的 data.json 文件
  3. 播放 → web 开发者使用 lottie-web 库解析该 JSON,并在浏览器中实时渲染动画

若想深入理解 Lottie 的动画机制,从 JSON Schema 入手是一条清晰的路径。Lottie 是一个完全由数据驱动的动画系统,掌握其 JSON 结构,就等于握住了理解动画渲染逻辑的钥匙。当然,Lottie JSON 的配置项较为丰富,不必追求一次性全部记住。本文旨在帮助大家建立初步认知,为后续学习渲染引擎打下基础,做到"知其然,亦知其所以然"。

一、初识 data.json :动画数据的顶层结构

Lottie 动画的本质是一套由数据驱动的图形绘制指令集 ,而 data.json 文件正是这套指令的完整载体。其顶层结构 定义了动画的全局元信息,如同电影的"导演台本",设定了动画播放的画布尺寸、时间线与基础环境。理解这些字段,是后续深入解析图层、关键帧等复杂属性的根基。

顶层元数据:动画的全局定义

下表列出了 data.json 根对象(Root Object)中的核心元数据字段,它们共同框定了动画的时空属性与渲染基础。

字段 类型 描述 示例值 单位/备注
v string Bodymovin 插件版本:生成此 JSON 所用的插件版本号,不同版本可能支持不同的特性。 "5.7.4" -
fr number 帧率(Frame Rate) :动画的播放速率,直接决定动画的流畅度与时间计算基准。 30, 60 帧/秒 (fps)
ip number 起始帧(In Point) :动画时间轴开始的绝对帧编号。 0
op number 结束帧(Out Point) :动画时间轴结束的绝对帧编号。动画总时长 = (op - ip) / fr 秒。 90
w number 画布宽度:动画合成(Composition)的逻辑宽度。 800 像素 (px)
h number 画布高度:动画合成的逻辑高度。 600 像素 (px)
nm string 合成名称(Name) :对应 After Effects 中合成的名称,便于识别。 "元数据演示动画" -
ddd integer 3D 图层标识 :标识此合成中是否包含 3D 图层或摄像机0 代表否(默认),1 代表是。 0 -

综合以上字段,一个定义了800x600画布、30fps、时长3秒的2D动画,其顶层结构示例如下:

json 复制代码
{
  "v": "5.7.4",
  "fr": 30,
  "ip": 0,
  "op": 90,
  "w": 800,
  "h": 600,
  "nm": "元数据演示动画",
  "ddd": 0,
}

核心组成:图层、资源与字形

assetscharslayers 是构成 Lottie 动画内容的三个核心数据数组。它们分别定义了可复用的素材、矢量字形以及图层的序列与属性,共同描述了动画的全部视觉信息与动态行为。

assets - 可复用资源库

assets 数组用于集中定义动画中可被多次引用的静态或动态资源。此设计实现了资源复用,有助于优化文件体积并保证引用的一致性。

资源类型 类型标识 (ty) 内容描述 引用字段 主要用途
图像资源 "img" 包含图像ID、尺寸及文件路径或Base64编码数据。 refId 静态位图元素,如图标、背景。
预合成资源 "precomp" 一个完整的子合成,包含独立的图层(layers)与时间轴。 refId 可重复使用的动画片段,如通用动效组件。

引用机制 :图层通过其 refId 属性与 assets 中对应ID的资源进行关联。渲染时,引擎据此查找并绘制相应资源。

chars - 矢量字形定义

chars 数组存储了将文字转换为矢量形状后的数据。当动画需要对文字进行路径变形、描边动画等超越普通文本渲染能力的操作时,会使用此数组。

  • 产生条件:在After Effects中对文本图层执行"从文字创建形状",或在导出时启用"字符形状"选项。
  • 数据结构 :每个项定义了字符、字体信息及其矢量路径(data),该路径数据包含轮廓、描边与填充等属性。
  • 核心用途:实现对文字字符的精细化控制,用于制作字形变形、笔画动画等效果。

layers - 动画图层序列

layers 是一个有序数组,定义了所有图层的叠加顺序、基本属性及关键帧动画。它是组织动画渲染逻辑的主体。

  • 渲染顺序:数组索引从0开始,图层按索引升序从底层到顶层依次渲染。
  • 核心属性:每个图层对象包含类型(如形状、图像、文本)、空间变换属性、蒙版以及关键帧动画数据。
  • 关键作用assetschars 中定义的资源需在此被引用,并通过图层的动画属性驱动,才能成为最终动画的一部分。

协作关系assetschars 作为资源定义,为 layers 提供素材;layers 则负责组织这些素材,并通过其时间轴和属性控制动画的最终表现。一个完整的结构示例如下:

json 复制代码
{
  "v": "5.7.4",
  "fr": 30,
  "ip": 0,
  "op": 90,
  "w": 800,
  "h": 600,
  "nm": "元数据演示动画",
  "ddd": 0,
  "assets": [],
  "layers": [],
  "chars": []
}

二、图层系统 - 理解 Layers 结构⭐️

layers 数组是 Lottie JSON 中承载所有动画图层信息的主体。它定义了图层的堆叠顺序、属性及关键帧动画,是构成动画视觉呈现与动态行为的核心数据结构。Lottie 主要包含以下六种基础图层类型:

类型标识 (ty) 图层类型 核心描述
0 预合成图层 (Precomp Layer) 嵌套合成层,通过 refId 引用 assets 中定义的预合成资源,实现动画复用与模块化管理。
1 纯色图层 (Solid Layer) 纯色填充层,定义固定颜色的矩形区域,常用作背景或遮罩。
2 图像图层 (Image Layer) 静态图片层,通过 refId 引用 assets 中定义的图像资源。
3 空对象图层 (Null Layer) 不可见辅助层,主要用于通过其 ks 属性(变换数据)驱动子图层动画,或作为动画控制器。
4 形状图层 (Shape Layer) 矢量图形层,由路径、描边、填充等属性构成,是制作变形、路径动画的基础。
5 文本图层 (Text Layer) 文字图层,可定义字体、颜色、段落样式,并支持逐字符动画。

所有图层共享一组基础属性,用于定义其在合成中的基本状态与时空关系。

以下属性为所有图层类型共有的基础属性:

属性 类型 必选 描述 示例值 默认值
ty number 图层类型标识,决定图层的行为和数据结构(见上表) 0-5 -
nm string / number 图层名称,在 After Effects 中设置,用于表达式和调试 "按钮背景", "Layer 1" -
ind number 图层索引,在整个合成中唯一,用于父子关联和表达式引用 1, 2, 3 -
ip number 入点 (In Point),图层开始显示的帧数 0, 30 -
op number 出点 (Out Point),图层结束显示的帧数 90, 180 -
st number 起始时间 (Start Time),图层的时间偏移量 0, 10 -
sr number 时间拉伸 (Stretch),控制图层播放速度的系数 1 (正常), 0.5 (减速), 2 (加速) 1
ks object 变换属性 (Transform),包含位置、缩放、旋转、透明度等关键帧数据 见下方详解 -
ao number 自动定向 (Auto-Orient),沿路径自动调整图层方向(布尔值:0/1) 0 (关闭), 1 (开启) 0
ddd number 3D 图层标识,标记该图层是否为 3D 图层(布尔值:0/1) 0 (2D), 1 (3D) 0
parent number 父图层索引 ,指向父图层的 ind 值,实现层级控制和联动变换 1, 5 -
cl string CSS 类名 (Class),在 SVG/HTML 渲染器中作为 HTML class "background", "icon" -
ln string HTML ID,在 SVG/HTML 渲染器中作为 HTML id 属性 "layer-bg", "main-icon" -

以上13个通用属性中,有 3个ipopnm)与根对象中的概念完全一致 ,仅作用域不同。1个ddd)与根对象概念相似但有区别 :根对象的 ddd 表示合成"是否包含"3D元素,而图层的 ddd 表示其"本身是否为"3D图层。其余 9个ty, ind, st, sr, ks, ao, parent, cl, ln)则是图层专有的核心属性,它们共同定义了图层在时间与空间中的基本状态。

接下来,我们将首先深入解析其中最核心的 ks(变换属性) 对象,它是驱动所有图层运动与变化的关键。

三、变换属性 - 位置、旋转、缩放⭐️

ks 对象概览

ks(Transform)是所有图层的核心动画容器,它定义了图层在空间中的位置、大小、旋转、透明度等基础变换状态。其名称 ksKeyable Styled Properties(可关键帧的样式化属性) 的缩写,这精准概括了其两大核心特性:

  • k(Keyable) :指这些属性可被设置为关键帧,是实现所有逐帧动画的基础。
  • s(Styled) :指这些属性可被动态驱动或赋予样式,支持通过数据或表达式进行复杂控制。

因此,ks 对象不仅描述图层的静态空间状态,更是承载其所有运动与变换动画的数据载体。所有基础动画(如移动、缩放、旋转、淡入淡出)都通过在此对象内定义关键帧来实现。

ks 的完整属性表

属性 全称 默认值 作用
a Anchor Point [0, 0, 0] 锚点:图层变换的基准中心点(支点)。
p Position [0, 0, 0] 位置:图层在画布中的坐标。
s Scale [100, 100, 100] 缩放:图层的尺寸比例(以百分比表示)。
r Rotation 0 旋转:图层的旋转角度(单位为度)。
o Opacity 100 不透明度:图层的透明度(100为完全不透明,0为完全透明)。
sk Skew 0 倾斜:图层的倾斜角度(单位为度)。
sa Skew Axis 0 倾斜轴:定义倾斜操作所沿的轴向角度(单位为度)。

3D 图层特殊形式:当 ddd=1 时,p 可能被拆分为 px、py、pz 三个独立属性。

属性值的两种数据形态

每个变换属性的值均以统一的结构表示,分为 静态值动画值 两种形态,通过 a 字段区分。

1. 静态值 (a: 0)

属性在整个时间轴上保持不变,结构简洁。

json 复制代码
{
  "r": {
    "a": 0,        // 静态标识
    "k": 45        // 固定值:旋转45度
  }
}

2. 动画值 (a: 1)

属性值随时间变化,通过关键帧数组 (k) 定义。

json 复制代码
{
  "r": {
    "a": 1,        // 动画标识
    "k": [         // 关键帧数组
      {
        "t": 0,    // 时间点(帧)
        "s": [0],  // 起始值
        "e": [360] // 结束值
      },
      {
        "t": 60,   // 下一关键帧时间点
        "s": [360] // 该帧的数值(结束时省略"e")
      }
    ]
  }
}

关键帧核心字段说明

  • t (time) :关键帧所处的时间点(帧序号)。
  • s (start value) :此关键帧的起始值。
  • e (end value) :朝向下一关键帧的目标值。最后一个关键帧可省略。
  • i / o:定义属性变化速率(缓动)的贝塞尔曲线控制点,将在后续章节详述。

此结构是 Lottie 实现所有基础运动(位移、缩放、旋转、淡入淡出)的通用数据范式。

🎨小试牛刀:动手验证核心概念

通过前面的学习,我们已经掌握了 Lottie JSON 的顶层结构和核心属性。现在,让我们通过一个具体的例子,动手验证如何配置一个基础的静态场景。

在本例中,我们将使用一张图片,创建一个静态的图片图层。您将在下图中看到(一个静态图,一个旋转图):

说明 :上图的 GIF 展示了包含旋转动画的完整效果。而下面的 JSON 配置,我们将首先完成左侧静态图片 的搭建。这能让我们专注于已学的静态属性配置a: 0)。右侧的旋转动画,将在我们学习了关键帧系统后,通过简单地修改 r(旋转)属性即可实现。

以下是完整的 Lottie JSON 配置,已附上详尽的注释:

js 复制代码
{
  // ========== 顶层元数据:动画的全局定义 ==========
  "v": "5.7.4",              // Bodymovin 插件版本
  "fr": 30,                  // 帧率:30fps
  "ip": 0,                   // 起始帧:从第0帧开始
  "op": 90,                  // 结束帧:到第90帧结束(总时长 = (90-0)/30 = 3秒)
  "w": 800,                  // 画布宽度:800像素
  "h": 600,                  // 画布高度:600像素
  "nm": "变换属性演示",        // 合成名称
  "ddd": 0,                  // 3D标识:0表示不包含3D图层
  
  // ========== Assets:可复用资源库 ==========
  "assets": [
    {
      "id": "image_0",       // 资源ID,供图层通过 refId 引用
      "w": 500,              // 图片原始宽度
      "h": 500,              // 图片原始高度
      "u": "",               // 图片URL基础路径(空表示使用完整路径)
      "p": "https://img11.360buyimg.com/img/jfs/t1/383242/14/16667/276460/695dd5ccF565013e8/02761c81c82b92d4.png",  // 图片完整URL
      "e": 0                 // 是否嵌入:0表示外部链接
    }
  ],
  
  // ========== Layers:图层序列(从下到上渲染) ==========
  "layers": [
    // ---------- 图层1:静态图片(展示静态值 a:0) ----------
    {
      "ddd": 0,              // 该图层不是3D图层
      "ind": 1,              // 图层索引:1(唯一标识)
      "ty": 2,               // 图层类型:2 = Image Layer(图像图层)
      "nm": "静态图片",       // 图层名称
      "refId": "image_0",    // 引用 assets 中 id 为 "image_0" 的资源
      "sr": 1,               // 时间拉伸:1 = 正常速度
      
      // ===== ks:变换属性(Transform)=====
      "ks": {
        // ----- o:不透明度(Opacity)-----
        "o": {
          "a": 0,            // 静态值标识(a=0 表示不动画)
          "k": 100           // 固定值:100%不透明
        },
        
        // ----- r:旋转(Rotation)-----
        "r": {
          "a": 0,            // 静态值标识
          "k": 45            // 固定值:旋转45度
        },
        
        // ----- p:位置(Position)-----
        "p": {
          "a": 0,            // 静态值标识
          "k": [250, 300, 0] // 固定位置:[x, y, z] = 左侧250px,顶部300px
        },
        
        // ----- a:锚点(Anchor Point)-----
        "a": {
          "a": 0,            // 静态值标识
          "k": [250, 250, 0] // 锚点在图片中心(500x500图片的中心点)
        },
        
        // ----- s:缩放(Scale)-----
        "s": {
          "a": 0,            // 静态值标识
          "k": [30, 30, 100] // 缩放到30%(显示为150x150)
        }
      },
      
      "ao": 0,               // 自动定向:关闭
      "ip": 0,               // 图层入点:第0帧开始显示
      "op": 90,              // 图层出点:第90帧结束显示
      "st": 0,               // 起始时间偏移:0(无偏移)
      "bm": 0                // 混合模式:0 = Normal(正常)
    }
  ],
  
  // ========== Chars:矢量字形定义(本例未使用)==========
  "chars": []
}

这个示例是一个"知识检查点",清晰地展示了:

  • 顶层元数据 (v, fr, w, h...) 定义了动画的舞台(3秒、800x600的画布)。
  • 资源 (assets) 定义了唯一可用的图片素材,并通过 id 标识。
  • 图层 (layers) 通过 refId 引用该资源,将其实例化到舞台上。
  • ks 变换对象 决定了这个实例的最终状态:位于画布左侧,缩小至30%,并旋转了45度。所有属性均以静态值 (a: 0) 定义。

至此,您已经掌握了如何构建一个 Lottie 动画的静态骨架 ------定义舞台、准备素材、放置元素并设置其初始状态。在上方的预览图中,您也看到了动画的潜力:只需将 r(旋转)属性的 a 值从 0 改为 1,并配上关键帧数据,静态图片就能旋转起来。

那么,a: 1 模式下的 k 数组究竟如何定义?多个关键帧之间如何平滑过渡?动画的运动节奏又由什么控制?接下来,我们就将深入动画的核心,解析关键帧、插值与缓动的完整系统。


四、深入动画 - 关键帧、插值与缓动⭐️

在上一章的"小试牛刀"中,我们看到了静态图片与旋转图片的对比效果。右侧的旋转效果正是通过关键帧动画实现的。现在,让我们深入解析这个旋转动画的完整JSON配置,揭开Lottie动画系统的核心机制。

让我们对比一下 demo 中两个图层的配置,看看静态图片是如何"动"起来的:

静态图片(左侧):

json 复制代码
"r": {
  "a": 0,            // 静态值标识
  "k": 45            // 固定值:旋转45度
}

旋转图片(右侧):

json 复制代码
"r": {
  "a": 1,            // 动画值标识(a=1 表示有关键帧动画)
  "k": [             // 关键帧数组
    {
      "i": { "x": [0.667], "y": [1] },   // 入缓动
      "o": { "x": [0.333], "y": [0] },   // 出缓动
      "t": 0,                            // 时间点:第0帧
      "s": [0]                           // 起始值:0度
    },
    {
      "t": 90,                           // 时间点:第90帧
      "s": [360]                         // 结束值:360度
    }
  ]
}

核心差异总结:

特征 静态值 (a: 0) 动画值 (a: 1)
k 的类型 单个数值或数组 关键帧对象数组
时间轴 无时间概念,始终保持固定值 在时间轴上定义多个状态点
缓动控制 通过 io 控制运动节奏
适用场景 静态属性(如固定位置、固定角度) 需要随时间变化的属性(移动、旋转、缩放等)

关键帧数组结构解析

当属性设置为动画值(a: 1)时,k 字段不再是一个简单值,而是一个关键帧对象数组。每个关键帧对象定义了动画在特定时间点的状态。

核心字段说明

字段 类型 必选 描述
t number 时间点(Time):该关键帧在时间轴上的位置,单位为帧。
s array 起始值(Start Value) :该关键帧的属性值。对于旋转是 [角度],位置是 [x, y, z]
e array 结束值(End Value) :从当前关键帧到下一关键帧的目标值。通常省略 ,渲染器会自动使用下一关键帧的 s 值。
i object 入缓动(In Tangent) :定义进入当前关键帧时的速度变化曲线(贝塞尔控制点)。
o object 出缓动(Out Tangent) :定义离开当前关键帧时的速度变化曲线(贝塞尔控制点)。
h number 保持帧(Hold) :值为 1 时,表示该关键帧到下一关键帧之间不插值,保持当前值(阶跃动画)。

说明

  1. s 的数据类型 :虽然官方 schema 中定义为 number,但在实际使用中,s 通常是数组 (如 [0] 表示旋转 0 度,[100, 200, 0] 表示位置)。这是为了统一处理单维和多维属性。
  2. e 字段的实际使用 :在大多数情况下,e 字段会被省略,渲染器会自动从下一个关键帧的 s 值推断目标值。只有在需要显式控制插值目标时才会使用。
  3. 最后一帧 :最后一个关键帧通常只有 ts,不需要 ioe(因为没有下一个关键帧)。

缓动控制点的结构

io 对象定义了贝塞尔曲线的控制点,用于控制动画的加速度:

json 复制代码
{
  "i": {
    "x": [0.667],  // 入缓动的 X 轴控制点(时间维度,范围 0-1)
    "y": [1]       // 入缓动的 Y 轴控制点(数值维度,通常 0-1,但可超出)
  },
  "o": {
    "x": [0.333],  // 出缓动的 X 轴控制点
    "y": [0]       // 出缓动的 Y 轴控制点
  }
}
  • x 数组 :控制时间进度的变化率(水平方向)
  • y 数组 :控制数值变化的速率(垂直方向)
  • 数组长度 :对于单维属性(如旋转 r),数组长度为 1;对于多维属性(如位置 p),数组长度对应维度数(如 [0.5, 0.3] 表示 X、Y 两个维度的独立控制)

前端知识关联io 定义的贝塞尔曲线,其作用与 CSS 中的 transition-timing-functionanimation-timing-function 完全一致,用于创造非匀速的动画效果。区别在于,CSS 使用一个二维的 cubic-bezier(x1, y1, x2, y2),而 Lottie 的 io 允许为每个属性维度单独定义控制点,控制更为精细。

工具推荐 :如果您需要直观地创建或理解贝塞尔曲线,强烈推荐使用在线工具 cubic-bezier.com 。您可以在那里调整曲线并获取对应的 cubic-bezier() 值,其原理与 Lottie 的 i/o 控制点相通,是理解和调试动画缓动的绝佳助手。

关键帧的工作流程

以 demo 中的旋转动画为例,让我们逐步拆解渲染器如何处理关键帧:

第 1 步:解析关键帧数组

json 复制代码
"k": [
  {
    "t": 0,        // 第0帧
    "s": [0],      // 旋转角度 0°
    "i": { "x": [0.667], "y": [1] },
    "o": { "x": [0.333], "y": [0] }
  },
  {
    "t": 90,       // 第90帧
    "s": [360]     // 旋转角度 360°
  }
]

渲染器识别出:

  • 动画从第 0 帧开始,到第 90 帧结束
  • 起始角度 0°,结束角度 360°
  • 需要在这 90 帧之间进行插值计算

第 2 步:插值计算(以第 45 帧为例)

当播放到第 45 帧时,渲染器需要计算此时的旋转角度:

  1. 计算时间进度progress = (45 - 0) / (90 - 0) = 0.5(已完成 50%)
  2. 应用缓动函数 :根据 io 的贝塞尔控制点,将线性进度 0.5 转换为缓动后的进度(假设为 0.55
  3. 计算属性值angle = 0 + (360 - 0) × 0.55 = 198°

第 3 步:渲染当前帧

渲染器将计算出的 198° 应用到图层的旋转属性,完成该帧的绘制。

关键帧的连续性

在多个关键帧的场景中,渲染器会:

  • 定位当前区间:找到当前时间点所在的关键帧区间(如第 30 帧位于第 0 帧和第 90 帧之间)
  • 使用对应缓动 :应用该区间起始关键帧的 o(出缓动)和结束关键帧的 i(入缓动)
  • 独立插值 :对于多维属性(如位置 [x, y, z]),每个维度独立进行插值计算

本章小结

至此,我们已经掌握了 Lottie 动画系统的"动力源":关键帧、插值与缓动

  • 静态与动画的开关 :通过 a 字段 (01) 切换属性的静态与动态模式,是理解 Lottie 动画逻辑的第一课。
  • 关键帧定义状态k 数组中的每个关键帧对象,通过 t (时间) 和 s (数值) 在时间轴上锚定了动画的各个"关键时刻"。
  • 缓动赋予灵魂i (入缓动) 与 o (出缓动) 所定义的贝塞尔曲线,控制了数值变化的速率与节奏,是让动画摆脱机械感、获得生命力的关键。
  • 渲染器执行插值 :在关键帧之间,Lottie 渲染器会依据缓动曲线,为每一帧实时计算出精确的属性值,从而创造出平滑的动画效果。

您已经了解了从定义到渲染的完整链条。关于贝塞尔曲线的数学原理多维属性的独立插值策略 以及更复杂的表达式动画,都属于更深入的话题。掌握了本章的核心数据模型,您已经具备了自行解析绝大多数 Lottie 动画、并理解其运动逻辑的能力。


五、形状系统 - 矢量图形

在 Lottie 的六种基础图层中,形状图层(Shape Layer,ty: 4 占据着独特而核心的地位。其他图层类型------如空对象图层(用于控制)、预合成图层(用于嵌套)、图像图层(静态位图)和文本图层(字形动画)------虽然在特定场景下不可或缺,但它们在数据结构和动画能力上相对简单。

形状图层(ty: 4)是 Lottie 实现复杂矢量动画的核心。与其他图层类型相比,它拥有最丰富的专有属性和最强的动画表现力。

形状图层的结构:ty: 4shapes 数组

当一个图层的 ty 值为 4 时,它就是一个形状图层。其核心数据容器是 shapes 数组(在早期的 Lottie 版本中可能标记为 it)。该数组是一个有序的列表,定义了构成最终矢量图形的所有基础元素,例如路径、描边、填充等。

  • 有序渲染与叠加shapes 数组中的元素严格遵循数组索引顺序(从 0n-1)进行渲染 。在视觉上,这意味着索引值更大的元素(后渲染)会叠加在索引值更小的元素(先渲染)之上 。例如,shapes[2] 会覆盖在 shapes[0]shapes[1] 之上。这一规则是组织复杂图形层级的基础。
  • 元素类型 :每个元素都是一个独立的对象,并通过 ty 字段来声明自己的类型(如 gr 表示组,sh 表示路径等)。

四种基础形状类型速览

shapes 数组中可以包含多种图形元素,其中基础形状主要有以下四种:

类型标识 (ty) 名称 描述 核心动画属性
rc 矩形 定义矩形或圆角矩形。 p (位置), s (尺寸), r (圆角半径)
el 椭圆 定义圆形或椭圆形。 p (中心点), s (半径/尺寸)
sr 星形/多边形 定义星形或多边形,可控制角数、内外径等。 p (中心点), ir/or (内/外半径), pt (角数)
sh 自由路径 由贝塞尔曲线构成的任意形状路径,是矢量图形的基础。 ks (路径数据,包含顶点与贝塞尔控制点)

说明rc, el, sr 本质上是参数化形状,它们会在导出时被转换为最终的 sh(路径)数据。但在 JSON 中,它们作为逻辑元素存在,便于理解和编辑。

自由路径的核心:顶点与贝塞尔控制点

自由路径(ty: 'sh')是最基础、最灵活的形状元素。其核心数据存储在 ks 属性中,它定义了一系列顶点(Vertex) 以及连接这些顶点的贝塞尔曲线

一个路径的关键帧数据通常包含以下字段:

json 复制代码
{
  "a": 1, // 表示路径数据是动画的
  "k": {
    "i": [[0, 0]], // 入控制点 (In Tangent)
    "o": [[0, 0]], // 出控制点 (Out Tangent)
    "v": [[50, 50]], // 顶点 (Vertex)
    "c": true // 路径是否闭合 (Closed)
  }
}
  • v (顶点) :一个二维数组,定义了路径在空间中经过的关键点坐标 [x, y]
  • i (入控制点) :定义曲线进入当前顶点时的方向与张力。
  • o (出控制点) :定义曲线离开当前顶点时的方向与张力。
  • c (闭合) :布尔值,true 表示路径的首尾顶点应连接,形成封闭图形。

工作原理v 定义了"骨架",io 则定义了连接骨架的"肌肉"曲线。通过为这些数据添加关键帧,即可实现路径的形变、绘制等复杂动画。

知识关联:两种贝塞尔曲线

您可能已经发现,这里的 io 与第四章关键帧缓动 中的 io 字段同名,且都代表贝塞尔曲线的控制点。这是 Lottie 中贝塞尔曲线的两种核心应用:

维度 空间贝塞尔曲线 (本章) 时间贝塞尔曲线 (第四章)
作用 定义空间中 的图形形状 定义时间上 的属性变化速率(缓动)。
控制点 (i/o) 控制顶点处曲线的方向与曲率,决定路径形态。 控制关键帧处动画速度的快慢,决定运动节奏。
坐标空间 位于画布的二维/三维空间 (如 [x, y])。 位于时间-进度二维空间,X轴是时间(0-1),Y轴是进度(0-1)。
直观感受 拖拽控制柄,改变的是线的弯曲程度 拖拽控制柄,改变的是动画的先快后慢

底层一致性 :尽管应用不同,但两者都基于三次贝塞尔曲线 的数学模型。理解这一点后,无论是调整路径平滑度还是动画缓动,您操作的都是同一种"控制点"逻辑。这也解释了为何可视化工具 cubic-bezier.com 对理解两者都有帮助:虽然它主要用于缓动(时间),但其对曲线形态的直观展示,同样有助于您想象空间路径中控制点对形状的影响。

形状组(gr):嵌套与组织

为了管理复杂的图形,Lottie 引入了 形状组(ty: 'gr' 。组可以将多个形状元素(包括其他组)打包为一个逻辑整体。

  • it 数组:组的核心属性,是一个数组,用于包含其子元素(如路径、填充、描边或其他组)。

  • 作用

    1. 层次化管理:像文件夹一样组织图形,使结构清晰。
    2. 统一变换 :组可以拥有自己的 ks(变换)属性。对该组应用的变换(如移动、缩放)会同时影响其内部所有子元素。
    3. 动画复用:通过控制组的变换,可以轻松实现整个图形模块的动画。

六、形状样式 - 填充与描边

在上一章中,我们探索了形状图层如何通过路径、椭圆、矩形等元素定义图形的几何骨架 。然而,只有几何形状是"不可见"的。要让图形真正被渲染出来,就需要为其赋予样式

本章将介绍形状系统中负责视觉呈现的两大核心样式元素:填充(Fill)描边(Stroke) 。它们为形状的内部轮廓提供颜色、渐变和不透明度等视觉效果,是矢量图形从"线框"变为"画面"的关键。

6.1 样式的作用:让形状可见

shapes 数组中,样式元素(如 flst)与形状元素(如 shel)地位平等,通过渲染顺序相互结合。

  • 堆叠规则it 数组中的元素按索引顺序依次绘制 。这意味着索引更大的元素(后绘制的)会叠加在索引更小的元素(先绘制的)之上。在图形构建中,通常先定义"形状"元素(如 el, sh),再定义为其着色的"样式"元素(如 fl, st)。
  • 独立性与组合性:样式元素是独立的,可以自由组合。一个形状可以同时拥有填充和描边,也可以只有其中一种。它们共同附着于其上方最近且未闭合的图形元素或组。

填充(Fill):纯色与渐变

填充用于为形状的内部区域着色。Lottie 支持两种填充类型。

纯色填充(ty: 'fl')

这是最基础的填充类型,使用单一颜色。

json 复制代码
{
  "ty": "fl", // 类型:Fill (纯色填充)
  "c": {      // Color (颜色)
    "a": 0,
    "k": [0.2, 0.6, 1, 1] // RGBA 数组,值范围 0-1
  },
  "o": { ... } // Opacity (不透明度)
}

渐变填充(ty: 'gf')

Lottie 支持线性渐变和径向渐变,为填充带来丰富的色彩过渡。

json 复制代码
{
  "ty": "gf", // 类型:Gradient Fill (渐变填充)
  "t": 1,     // 渐变类型:1-线性,2-径向
  "g": {
      "p": 2,
      "k": {
        "a": 0,
        "k": [0, 0.2, 0.6, 1, 1, 1, 0, 1, 0, 1]
      }
    }
  "s": { ... }, // 起点 (线性) / 起始点 (径向)
  "e": { ... }  // 终点 (线性) / 结束点 (径向)
}
  • g 对象 :是渐变的核心。p 定义色标数量,k 是一个扁平的数组,每 5 个数字为一组,表示一个色标的 [位置, R, G, B, A]。所有色标数据按顺序连接。

描边(Stroke):轮廓与样式

描边用于绘制形状的轮廓线。它拥有比填充更丰富的属性来控制线条的视觉表现。

纯色描边(ty: 'st')

json 复制代码
{
  "ty": "st", // 类型:Stroke (描边)
  "c": { ... }, // 颜色,同填充
  "w": {      // Width (线宽)
    "a": 0,
    "k": 5    // 线宽为5像素
  },
  "lc": 2,    // Line Cap (线帽): 1-平头, 2-圆头, 3-方头
  "lj": 2,    // Line Join (连接): 1-斜接, 2-圆角, 3-斜面
  "ml": 10,   // Miter Limit (斜接限制)
  "o": { ... } // 不透明度
}
  • w (Width) :描边的粗细,支持动画。
  • lc (Line Cap) :定义线段端点的样式。
  • lj (Line Join) :定义线段转折处的连接样式。
  • ml (Miter Limit) :当 lj 为 1 (斜接) 时,控制斜接长度与线宽的比例上限,防止尖角过长。

渐变描边(ty: 'gs')

渐变描边的数据结构与渐变填充 (gf) 高度相似,包含 t (类型)、g (渐变数据) 等属性,区别在于它应用于轮廓线而非填充区域。

样式的通用属性

填充和描边共享一些控制其最终呈现效果的通用属性:

属性 类型 描述
o object 不透明度 (Opacity) 。通过 ak 控制,值范围为 0 到 100(100 为完全不透明)。这与 CSS 中 0-1 的范围不同,请注意区分。
bm number 混合模式 (Blend Mode) 。定义当前样式如何与下方已有的像素进行混合。常见值:0 (正常)、1 (相乘)、2 (屏幕) 等,对应 After Effects 中的混合模式。

🎨小试牛刀:画个圆

现在,让我们综合运用以上概念,创建一个最简单的形状:一个蓝色的实心圆 。我们将通过 JSON 配置,清晰地展示形状图层的 shapes 数组是如何组织起来的。

以下是实现该圆形的精简版 JSON 配置 ,我们省略了前面章节已详细讲解的通用图层属性(如 ks 变换),将焦点完全放在形状图层特有的 shapes 数组上:

js 复制代码
{
  "v": "5.7.4",
  "fr": 30,
  "ip": 0,
  "op": 90,
  "w": 800,
  "h": 600,
  "layers": [
    {
      "ty": 4, // ⭐️ 核心标识:这是一个形状图层
      "nm": "带描边圆形",
      "ind": 1,
      // 🔽 此处省略了图层的 `ks` 变换属性(如 p, a, s, r, o)
      // 在完整文件中,它们用于将整个图层定位在画布中心,例如:
      // "ks": { "p": { "a": 0, "k": [400, 300, 0] }, ... }
      
      "shapes": [ // ⭐️ 本章核心:形状与样式数组
        {
          "ty": "gr", // 类型:gr (Group),一个形状组
          "nm": "圆形组",
          "it": [ // 组内元素列表,按索引顺序 0→1→2→3 渲染
            // 1. 形状定义:椭圆 (el) - 先绘制,定义几何轮廓
            {
              "ty": "el",
              "nm": "椭圆路径",
              "p": { "a": 0, "k": [0, 0] }, // 位置:相对于组的中心
              "s": { "a": 0, "k": [100, 100] } // 尺寸:宽高100px,即圆形
            },
            // 2. 样式定义:填充 (fl) - 其次绘制,为形状内部着色
            {
              "ty": "fl",
              "nm": "蓝色填充",
              "c": { 
                "a": 0, 
                "k": [0.2, 0.6, 1, 1] // 颜色:RGBA,此为蓝色
              },
              "o": { "a": 0, "k": 100 } // 不透明度:100%
            },
            // 3. 样式定义:描边 (st) - 最后绘制,为形状轮廓添加边线
            {
              "ty": "st",
              "nm": "白色描边",
              "c": { 
                "a": 0, 
                "k": [1, 1, 1, 1] // 颜色:白色
              },
              "w": { "a": 0, "k": 5 }, // 线宽:5像素
              "lc": 2, // 线帽:2 = 圆头
              "lj": 2  // 连接:2 = 圆角
            },
            // 4. 必需的组变换 (tr) - 必须放在最后,控制整个组的变换
            {
              "ty": "tr",
              "nm": "组变换"
              // 其内部属性 (p, a, s, r, o) 通常保持默认值 [0, 0, 100, 0, 100]
            }
          ]
        }
      ]
    }
  ]
}

七、形状修改器 - Trim、Repeater

在掌握了形状的构建与样式之后,我们迎来了形状系统的最后一块拼图:修改器(Modifiers) 。它们不是独立形状,而是附加在现有形状或组之上的"效果处理器",能够以非破坏性的方式动态改变图形的外观或行为,是实现复杂、程式化动画的关键。

修改器的概念:作用范围与顺序

修改器是一种特殊类型的元素,其 ty 值定义在 shapesit 数组中。它们不直接渲染 ,而是像一个处理器,作用于排列在它之前的特定元素

  • 作用逻辑 :在 it 数组中,修改器按照从前往后 的顺序执行,每个修改器会作用于它之前已定义 的特定元素。例如,Trim Path 裁剪其之前的路径,Repeater 重复其之前的整个形状组合。
  • 顺序关键 :修改器的位置 决定了其作用范围和最终效果。例如,[形状 → 样式 → Trim] 会裁剪已着色的形状;而 [形状 → Trim → 样式] 会先裁剪路径,再为裁剪后的部分着色。

Trim Path(tm):路径裁剪动画

Trim Path(路径裁剪)是最常用的修改器之一,它通过控制路径的"起止点"来创造笔触绘制、擦除、扫描等动画效果。

核心属性:

json 复制代码
{
  "ty": "tm", // 类型:Trim Paths
  "s": { // Start(起点百分比)
    "a": 1,
    "k": [{ "t":0, "s":[0] }, { "t":90, "s":[100] }] // 从0%到100%
  },
  "e": { // End(终点百分比)
    "a": 1,
    "k": [{ "t":0, "s":[100] }, { "t":90, "s":[100] }] // 保持100%
  },
  "o": { // Offset(偏移)
    "a": 0,
    "k": 0 // 范围0-360°,整体偏移裁剪区域
  }
}
  • s (Start) :路径可见部分的起点,范围 0-100%。动画化此属性可实现"生长"动画。
  • e (End) :路径可见部分的终点 ,范围 0-100%。通常 e >= s
  • o (Offset)裁剪区域的整体偏移量,范围 0-360°。它可以让裁剪的起止点沿路径循环移动,常用于创建"追逐"或"循环扫描"效果。

典型应用 :通过动画 se 属性,可以实现经典的"笔画书写"或"进度条填充"效果。

Repeater(rp):重复器

Repeater(重复器)能将它之前的所有图形元素 (包括形状、样式甚至其他修改器)复制多次,并对每个副本应用递增的变换,快速创建阵列、放射状、循环等复杂图案。

核心属性:

json 复制代码
{
  "ty": "rp", // 类型:Repeater
  "c": { // Copies(副本数量)
    "a": 0,
    "k": 5 // 生成5个副本(包含原始图形)
  },
  "o": { // Offset(副本索引偏移)
    "a": 0,
    "k": 0 // 控制从哪个"虚拟副本"开始渲染,可用于动画
  },
  "m": 1, // Composite(合成模式):1=Above(后续副本在上方),2=Below(后续副本在下方)
  "tr": { // Transform(每个副本的增量变换)
    "p": { "a":0, "k":[20, 0] }, // 位置增量:每个副本右移20px
    "s": { "a":0, "k":[90, 90] }, // 缩放增量:每个副本缩小至90%
    "r": { "a":0, "k":[30] } // 旋转增量:每个副本旋转30度
  }
}
  • c (Copies) :生成的副本总数(包含原始图形)。
  • o (Offset) :控制从哪个"虚拟副本"开始渲染,可用于动画。
  • m (Composite) :控制副本的堆叠顺序。1 表示新副本叠在上方,2 表示新副本叠在下方。
  • tr (Transform) :定义每个新副本相对于前一个副本的变换增量,是创造规律性变化的关键。

其他修改器简介

除了上述两个,Lottie 还提供了其他实用的修改器来扩展图形能力:

类型标识 (ty) 名称 核心作用 关键属性与备注
rd Round Corners (圆角) 将路径的所有尖角转换为指定半径的圆角。 r:圆角半径。
mm Merge Paths (合并路径) 将多个路径合并为一个(类似布尔运算)。 mm:合并模式(如相加、相减、交集等)。 ⚠️ 注意 :官方文档标注此功能目前不被支持,使用时需谨慎测试。

🎨小试牛刀:loading动画

让我们将学到的 Trim Path 知识应用到实践中,制作一个经典的 loading 动画。下面的示例将展示如何通过动画 o(偏移)属性来创建持续旋转的圆环进度效果。

js 复制代码
{
  "v": "5.7.4",
  "fr": 30,
  "ip": 0,
  "op": 90,
  "w": 800,
  "h": 600,
  "nm": "Trim Path演示",
  "layers": [
    {
      "ty": 4,
      "nm": "圆形绘制动画",
      "ind": 1,
      "ks": {
        "p": { "a": 0, "k": [400, 300, 0] }
      },
      "shapes": [
        {
          "ty": "gr",
          "nm": "圆形组",
          "it": [
            // 1. 圆形路径
            {
              "ty": "el",
              "nm": "圆形路径",
              "p": { "a": 0, "k": [0, 0] },
              "s": { "a": 0, "k": [200, 200] }
            },
            // 2. 描边样式
            {
              "ty": "st",
              "nm": "描边",
              "c": { "a": 0, "k": [0.2, 0.8, 1, 1] },
              "w": { "a": 0, "k": 12 },
              "lc": 2,
              "lj": 2
            },
            // 3. Trim Path 修改器 ⭐️
            {
              "ty": "tm",
              "nm": "Trim Path",
              "s": {  // 起点百分比 (0-100)
                "a": 1,
                "k": [
                  { "t": 0, "s": [0], "e": [100] },
                  { "t": 90, "s": [100] }
                ]
              },
              "e": {  // 终点百分比 (0-100)
                "a": 0,
                "k": 100
              },
              "o": {  // 偏移角度 (0-360)
                "a": 0,
                "k": 0
              }
            },
            // 4. 组变换
            { "ty": "tr" }
          ]
        }
      ]
    }
  ]
}

八、高级特性 - 蒙版、效果、表达式

本章将简要介绍 Lottie 中几个高级但常用的特性。理解这些概念有助于您阅读和分析更复杂的动画文件,但在实际创作中,请注意它们在不同平台和渲染器中的支持程度可能有所差异。

蒙版(Mask):masksProperties 数组

蒙版用于控制图层的显示区域,实现剪切、遮罩等效果。在图层对象中,通过 masksProperties 数组定义。

json

json 复制代码
"masksProperties": [
  {
    "mode": "a",           // 蒙版模式:a=相加,s=相减,i=相交等
    "pt": {                // 路径(Path),定义蒙版形状
      "a": 0,
      "k": { "v": [[0,0], [100,0], [100,100], [0,100]], "c": true }
    },
    "o": { "a": 0, "k": 100 },  // 不透明度(Opacity)
    "inv": false,                // 是否反转(Inverted)
    "nm": "蒙版1"                // 名称(Name)
  }
]

关键属性:

  • mode :蒙版混合模式。除了常用的 "a"(Add,相加)和 "s"(Subtract,相减),Lottie Schema 还定义了其他模式,但并非所有都被完全支持。

    模式代码 名称 (英文) 作用效果
    "n" None (无) 禁用蒙版
    "a" Add (相加) 合并多个蒙版区域
    "s" Subtract (相减) 从现有区域中减去
    "i" Intersect (相交) 只保留蒙版重叠区域
    "l" Lighten (变亮) 保留较亮区域
    "d" Darken (变暗) 保留较暗区域
    "f" Difference (差异) 显示颜色差异区域
  • pt :蒙版路径,其数据结构(含 v, i, o)与形状图层中的自由路径(sh)完全相同。

  • o:蒙版的不透明度。

  • inv :布尔值,为 true 时反转蒙版区域。

效果(Effects):ef 数组

Lottie 支持部分 After Effects 内置效果,通过图层的 ef 数组定义。请注意,支持的效果非常有限,且并非所有AE效果都能被完美支持或渲染。

根据官方 Schema 文档,有明确定义的效果类型包括:

  • 填充 (Fill): ty: 21
  • 描边 (Stroke): ty: 22
  • 色调 (Tint): ty: 20
  • 三色调/专业色阶 (Tritone/Pro Levels): ty: 23

json

json 复制代码
"ef": [{
  "ty": 21,                 // 效果类型:21 = 填充 (Fill)
  "nm": "颜色叠加",
  "en": 1,                  // 启用 (Enabled)
  "ef": [{
    "ty": 10, 
    "nm": "颜色", 
    "v": { "a": 0, "k": [1, 0, 0, 1] } // 红色
  }]
}]

重要说明 :"高斯模糊 (ty: 29)"和"发光 (ty: 27)"等效果在公开的官方 Schema 文档中未有明确定义 。它们可能由 Bodymovin 插件导出,但不一定被所有 Lottie 渲染器支持,在实际使用前必须进行充分的兼容性测试。

表达式(Expression):x 字段

表达式是 After Effects 中用于创建属性间动态关联的脚本语言。在 Lottie 中,表达式可以存储在属性的 x 字段中。

json

css 复制代码
"p": {
  "a": 1,
  "k": [{"t":0, "s":[0,0]}, {"t":30, "s":[100,100]}],
  "x": "loopOut('cycle')"  // 表达式:循环播放动画
}

表达式的作用与局限性

  • 作用 :可以创建循环 (loopOut)、随机 (wiggle)、数学关联等复杂动画逻辑,无需大量关键帧。
  • 局限性平台支持极不完整。仅有少数最基础的表达式可能在部分平台上被识别,复杂的表达式通常会被忽略或导致动画错误。在需要跨平台稳定播放的动画中,应尽量避免使用表达式。

混合模式(Blend Mode):bm 字段

混合模式控制当前图层如何与下层图层进行颜色混合。在图层(根对象)或形状样式元素(如 fl, st)中通过 bm 字段定义。

json

json 复制代码
"bm": 3  // 叠加模式 (Overlay)

混合模式值速查表 (根据官方 /helpers/blendMode.json):

模式 (英文) 模式 (英文)
0 Normal (正常) 8 Hard Light (强光)
1 Multiply (相乘) 9 Soft Light (柔光)
2 Screen (屏幕) 10 Difference (差值)
3 Overlay (叠加) 11 Exclusion (排除)
4 Darken (变暗) 12 Hue (色相)
5 Lighten (变亮) 13 Saturation (饱和度)
6 Color Dodge (颜色减淡) 14 Color (颜色)
7 Color Burn (颜色加深) 15 Luminosity (明度)

注意:混合模式在 SVG/HTML 渲染器中支持较好,在其他渲染器(如 Canvas)或某些移动端平台中可能需要降级处理或不被支持。


九、结语

本文系统性地解析了 Lottie JSON 的核心数据结构。以下是全文知识要点的回顾与总结:

核心数据结构总览

模块 关键对象/字段 核心作用与要点
顶层结构 v, fr, ip, op, w, h 定义动画全局信息:版本、帧率、时间轴、画布尺寸。
assets, layers, chars 三大数据支柱:可复用资源、图层序列、矢量字形。
图层系统 ty (0-5) 标识六种图层类型:预合成、纯色、图像、空对象、形状、文本。
ind, parent, ip, op, st 控制图层索引、父子关系、时间属性(入点、出点、起始时间)。
ks 变换属性容器 ,包含锚点(a)、位置(p)、缩放(s)、旋转(r)、透明度(o)等。
动画系统 ks 下的 a 字段 属性动画开关:0为静态值,1为动画值(关键帧数组)。
关键帧 k 数组 定义动画轨迹,包含时间(t)、值(s/e)、缓动(i/o)。
形状系统 shapes 数组 形状图层的核心容器,元素按索引顺序渲染叠加。
ty: el/rc/sr/sh 基础图形:椭圆、矩形、星形、自由路径(贝塞尔曲线定义)。
ty: gr (组) 使用 it 数组组织子元素,实现层级管理与统一变换。
样式系统 ty: fl (填充) 定义形状填充色(纯色或渐变)。
ty: st (描边) 定义轮廓线样式,包括线宽(w)、端点(lc)、连接(lj)。
修改器 ty: tm (Trim) 路径裁剪,通过动画起点(s)、终点(e)、偏移(o)实现绘制效果。
ty: rp (Repeater) 图形重复器,通过副本数(c)和增量变换(tr)创建阵列。
高级特性 masksProperties 蒙版数组,通过路径(pt)和模式(mode)控制图层显示区域。
ef (效果), bm (混合模式) 实现滤镜与图层混合,需注意平台支持度
x (表达式) 支持简单表达式驱动属性,跨平台支持有限

十、写在最后:笔者的思考💡

在整理 Lottie 的技术细节时,我反复思考一个核心问题:我们看到的这套 JSON 结构,到底是由什么决定的?

最直接的答案是"为了在网页上播放"。但这只是目的,并未解释其形态。我的思路分两步推进:第一,是 Web 的渲染能力(如 Canvas)限制了它的设计;第二,是否有更底层的蓝本在主导结构。

通过逐项对比,我找到了更关键的依据。Lottie JSON 的结构,本质上是对 After Effects 内部动画数据模型的直接翻译。 例如,JSON 中的 ks 对象精确对应了 AE 图层的"变换"属性组,shapes 数组则完全复现了 AE 形状层的堆叠逻辑。设计者的首要任务,是为 AE 的动画状态提供一个无损且精确的数据描述格式

那么,Web 技术(如 Canvas/SVG)的作用是什么?我认为它主要扮演了 "支持度评估"与"性能优化"的角色。它并未改变数据描述的根本方式,而是基于实现难度与性能成本,划定了哪些 AE 高级功能可以(或不可以)被包含在这个格式中。例如,一些复杂的实时滤镜可能因性能考量而被排除。

这自然引向更深一层:AE 自身的这套强大模型又是如何建立的? 它并非凭空创造,而是对更早行业的数字化融合。其"合成"与"图层"概念源自电影工业 的胶片叠加流程;"关键帧"动画继承自传统手绘动画 的生产方式;而所有视觉变换的根基,则是计算机图形学提供的数学工具(如坐标变换、贝塞尔曲线)。

因此,学习 Lottie 最有效的方法,并非孤立记忆 JSON 字段,而是理解它作为"AE 模型的数据接口"这一定位 。掌握 AE 的核心概念,就能理解 Lottie 绝大部分的设计逻辑。这揭示了一种高效的学习路径:当面对一个出色的"技术转译层"时,直接研究它所转译的源系统,往往是理解其设计最快的方式。

相关推荐
卖火箭的小男孩2 小时前
# Flutter Provider 状态管理精讲(Vue 开发者视角)
前端
前端_yu小白2 小时前
react常用优化手段
前端·javascript·react.js·性能优化·usecallback·usememo
攀登的牵牛花2 小时前
前端向架构突围系列 - 框架设计(六):解析接口职责的单一与隔离
前端·架构
涵涵(互关)2 小时前
JavaScript 对大整数(超过 2^53 - 1)的精度丢失问题
java·javascript·vue.js
开开心心_Every2 小时前
离线黑白照片上色工具:操作简单效果逼真
java·服务器·前端·学习·edge·c#·powerpoint
Mintopia2 小时前
🌌 信任是否会成为未来的货币?
前端·人工智能·aigc
fqbqrr2 小时前
2601C++,模块导出分类
前端·c++
倚栏听风雨2 小时前
vscode 运用 ts 代码需要准备什么
前端
韩曙亮2 小时前
【Web APIs】浏览器本地存储 ① ( window.sessionStorage 本地存储 | window.localStorage 本地存储 )
服务器·前端·javascript·本地存储·localstorage·sessionstorage·web apis