2D 渲染模型:从原理到实现

47 阅读1小时+

2D 渲染模型(2D Rendering Model)定义了 2D 图形系统的核心能力,包括静态渲染模型和动态渲染模型两个部分。静态渲染模型描述单帧画面的构成方式,包括图层的几何与样式、图层间的影响关系、渲染效果与合成规则。动态渲染模型在静态基础上引入时间维度和交互能力,包括滑动行为、动画系统和事件处理机制。本文首先从模型层面分析共性原理,然后介绍 Figma、HTML/CSS、SVG、Lottie 等具体系统的实现。

静态渲染模型

静态渲染模型定义了单帧画面的构成规则。它回答三个核心问题:单个图层如何表达(几何与样式)、图层之间如何影响(继承与依赖)、以及渲染系统如何将这些图层组合为最终画面(效果、合成与优化)。

渲染单元

Geometry 和 Paint 定义一个渲染单元。Geometry 确定渲染的空间范围(哪些像素需要绘制),Paint 确定这些像素的颜色。渲染单元是 2D 渲染的最小单位,具备完整的独立渲染能力。

Geometry:在哪里画

Geometry 是计算结果,不是直接定义

渲染模型不直接定义 Geometry,而是定义 Shape(形状类型)+ Fill/Stroke(应用方式),然后计算出最终的 Geometry:

Shape + Fill  → Fill Geometry(填充区域)
Shape + Stroke → Stroke Geometry(描边区域,由 Stroke 参数扩展而来)

Shape:形状的声明

Shape 是几何的声明式定义,包含:

  • 矩形(Rect):位置 + 尺寸
  • 圆角矩形(RRect):矩形 + 每个角的圆角半径
  • 椭圆(Oval):边界矩形
  • 路径(Path):贝塞尔曲线序列
  • 文本(Text):字符序列 + 字体 + 排版参数

Fill 和 Stroke:两种 Geometry 生成方式

Fill 和 Stroke 将 Shape 转换为可渲染的 Geometry:

  • Fill:填充 Shape 的内部区域。对于封闭路径,内部区域由填充规则(Fill Rule)决定(NonZero 或 EvenOdd)
  • Stroke:沿 Shape 的边界扩展出带宽度的区域。Stroke 参数包括:
    • 宽度:线条粗细
    • 端点样式(Cap):线条端点形状
    • 连接样式(Join):线条转角形状
    • 虚线模式(Dash):实线段和间隔的模式

文本的特殊性

文本是"声明式 Shape"到"实际 Geometry"的二次转换:

文本字符串 + 字体 → 字形轮廓路径集合 → Fill/Stroke Geometry

文本渲染需要字体解析、排版计算,最终生成字形路径参与渲染。

Paint:画什么

Paint 的本质:定义空间中每个点的颜色

Paint 不是简单的"颜色值",而是一个函数:f(x, y) → Color。给定坐标 (x, y),返回该位置应该绘制的颜色。

  • 纯色f(x, y) = 常量颜色,与位置无关
  • 渐变f(x, y) = 根据 (x, y) 插值的颜色,颜色随位置变化
  • 图像f(x, y) = 图像在 (x, y) 的采样值,从图像数据中读取

这种抽象使得 Paint 可以表达任意复杂的颜色分布。

Paint 的类型

基于"如何计算颜色",Paint 分为三类:

  1. 纯色(Solid Color)

    • 定义:单一 RGBA 值
    • 函数:f(x, y) = color
  2. 渐变(Gradient)

    • 定义:颜色停止点(Color Stops) + 渐变类型
    • 函数:根据 (x, y) 到渐变轴的距离插值
    • 类型:
      • 线性渐变:沿直线方向插值
      • 径向渐变:从中心点向外插值
      • 角度渐变:围绕中心点旋转插值
  3. 图像(Image)

    • 定义:图像数据 + 变换矩阵 + 平铺模式
    • 函数:将 (x, y) 变换到图像坐标,采样像素值

坐标空间

Paint 函数的 (x, y) 定义在特定坐标空间中:

  • 对象空间(Object Space):相对于 Geometry 的边界框(如 CSS 渐变,元素移动时渐变跟随)
  • 用户空间(User Space):画布的绝对坐标(如 SVG 的 gradientUnits="userSpaceOnUse",元素移动时渐变不跟随)

坐标空间决定 Paint 如何响应元素的变换和位置变化。

小结

渲染单元由 Shape + Fill/Stroke + Paint 定义:Shape 声明几何,Fill/Stroke 计算 Geometry,Paint 定义颜色函数。Geometry 是"在哪些像素上绘制",Paint 是"这些像素的颜色是什么"。

渲染效果

渲染效果对渲染单元的像素进行处理,改变最终的视觉呈现。渲染效果分为三类:可见性控制(决定哪些像素可见)、混合模式(控制像素如何与其他内容合成)、后处理效果(对已渲染的像素进行二次处理,包括颜色变换和空间卷积)。

可见性控制

可见性控制决定渲染单元的哪些像素最终可见。它不改变像素的颜色,只改变像素的可见状态。

Opacity(透明度)

Opacity 是最简单的可见性控制,通过缩放像素的 Alpha 通道实现:

result.rgba = (src.r, src.g, src.b, src.a × opacity)

Opacity 是连续值(0-1),支持半透明效果。它作用于整个渲染单元,所有像素的透明度统一缩放。

Clip(裁剪)

Clip 通过几何边界裁剪渲染单元,边界外的像素完全不可见。裁剪是硬边界:

if (x, y) 在裁剪区域内:
    保留像素
else:
    丢弃像素

裁剪区域可以是矩形、路径、或多个裁剪区域的布尔运算结果。

Mask(遮罩)

Mask 使用另一个渲染单元的 Alpha 通道控制可见性。与 Clip 的硬边界不同,Mask 支持平滑的透明度过渡:

result.rgba = (src.r, src.g, src.b, src.a × mask.a)

Mask 的每个像素提供一个透明度系数,使得边缘可以平滑过渡(抗锯齿、渐变边缘)。

三者的对比

效果边界类型透明度应用场景
Opacity无边界全局统一淡入淡出动画
Clip硬边界二值(可见/不可见)容器裁剪、滚动区域
Mask软边界逐像素可变复杂形状裁剪、渐变过渡

混合模式

混合模式(Blend Mode)定义像素如何与背景合成。它是一个函数:f(src, dst) → result,src 是当前渲染单元的像素,dst 是背景像素。

Porter-Duff 合成

Porter-Duff 是基础的 Alpha 合成算法,定义了如何根据 Alpha 值混合颜色:

result = src.color × src.alpha + dst.color × (1 - src.alpha)

这是默认的 SrcOver 模式,新内容覆盖在背景上方。

扩展混合模式

扩展混合模式对颜色通道应用数学运算:

  • Multiply(正片叠底)result = src × dst,颜色变暗
  • Screen(滤色)result = 1 - (1 - src) × (1 - dst),颜色变亮
  • Overlay(叠加):根据背景亮度选择 Multiply 或 Screen
  • Darken/Lighten:选择较暗/较亮的颜色

混合模式的本质是定义颜色空间中的混合函数。不同模式产生不同的视觉效果(变暗、变亮、对比、颜色调整等)。

后处理效果

后处理效果对已渲染的像素进行变换,分为颜色域变换和空间域变换。

颜色域变换

颜色域变换对每个像素独立处理,不考虑周围像素。它是像素级函数:f(color) → color

常见的颜色变换:

  • 亮度(Brightness):缩放 RGB 值
  • 对比度(Contrast):拉伸或压缩颜色范围
  • 饱和度(Saturation):在 HSL 空间调整饱和度分量
  • 色相旋转(Hue Rotation):在 HSL 空间旋转色相
  • 反色(Invert)result = 1 - color

这些变换可以表示为颜色矩阵或查找表(LUT)。

空间域变换

空间域变换需要读取周围像素,是卷积运算:

result(x, y) = Σ kernel(i, j) × src(x + i, y + j)

模糊(Blur)

模糊是空间域变换的典型应用。高斯模糊使用高斯函数作为卷积核,对周围像素加权平均:

kernel(x, y) = exp(-(x² + y²) / (2σ²))

模糊半径(radius)控制卷积核的大小,半径越大模糊越强。

综合渲染效果

复杂的视觉效果由多种渲染效果组合实现,并且涉及多个渲染单元的协作。

外阴影(Drop Shadow)

外阴影需要反转当前节点的 Alpha 通道,生成阴影形状,然后应用偏移和模糊。渲染效果的组合包括:反转 Alpha、偏移、模糊。外阴影绘制在节点内容的下方。

内阴影(Inner Shadow)

内阴影使用当前节点的轮廓作为裁剪边界,在节点内部显示阴影效果。渲染效果的组合包括:反转 Alpha、偏移、模糊、使用节点轮廓裁剪。内阴影绘制在节点内容的上方。

背景模糊(Backdrop Blur)

背景模糊涉及的渲染单元是之前渲染的所有节点(包括兄弟节点、父节点的背景等)。渲染效果的组合包括:模糊、Clip。

小结

渲染效果扩展了渲染单元的视觉能力。可见性控制决定像素是否可见,混合模式决定像素如何与背景合成,后处理效果对像素进行数学变换。这些效果组合使用,构成丰富的视觉表现。

布局

布局定义渲染单元在空间中的位置、旋转、缩放。布局的最终结果是变换矩阵(Transform),决定渲染单元的屏幕坐标。

变换

变换(Transform)使用矩阵表示,包含平移、旋转、缩放等操作。渲染时,渲染单元应用变换矩阵确定最终的屏幕坐标。变换矩阵是布局的最终输出,问题是如何得到这个变换矩阵。

布局方式

有两种方式指定渲染单元的布局:

显式指定

直接指定变换或位置属性。常见的方式包括:

  • 直接设置变换矩阵
  • 设置绝对位置(如 CSS 的 position: absolute; top: 10px; left: 20px
  • 设置相对偏移(如 CSS 的 position: relative; top: 5px

显式指定直接明确,但在复杂界面中维护成本高:添加元素、改变内容、响应不同屏幕尺寸时,都需要手动调整其他元素的位置。

约束布局

通过约束规则声明元素之间的空间关系,系统自动计算变换。约束布局描述"应该是什么样",而非"如何实现"。常见的约束类型:

  • 线性排列:子元素按行或列排列,间距固定或自适应(如 CSS Flexbox、Figma Auto Layout)
  • 填充容器:元素尺寸自动适应父容器的可用空间
  • 相对定位:元素相对父容器或兄弟元素对齐(如"居中"、"距离边缘 10px")
  • 尺寸约束:元素按比例分配空间或固定大小

约束布局的本质是:将空间关系的声明转换为变换矩阵的计算。当约束或固有尺寸改变时,布局自动重新计算。触发更新的情况包括:添加/删除/重新排序元素、元素固有尺寸改变(如文本内容变化、图像加载)、布局约束改变(如改变排列方向、间距)、父容器可用空间改变(如窗口调整大小)。

布局计算

布局计算将布局方式转换为最终的变换矩阵。对于显式指定,计算相对简单(如相对偏移的累加)。对于约束布局,布局系统是一个函数:

输入:约束规则 + 内容固有尺寸
输出:每个渲染单元的变换矩阵

固有尺寸(Intrinsic Size)

某些元素的大小由内容决定:

  • 文本:由字符数量、字体、换行决定
  • 图像:由图像的原始分辨率决定
  • 容器:可能由子元素的总尺寸决定

布局计算需要这些固有尺寸作为输入。

计算方向

布局计算可以是自上而下或自下而上,取决于约束的类型:

自上而下:父容器决定子元素的尺寸和位置。父容器先确定自己的尺寸(如固定宽度或填充屏幕),然后将空间分配给子元素。

父容器(固定 400px)
  → 子元素 A(分配 200px)
  → 子元素 B(分配 200px

自下而上:父容器的尺寸由子元素决定。子元素先根据内容确定固有尺寸,父容器根据子元素的尺寸计算自己的尺寸。

子元素 A(固有尺寸 150px)
子元素 B(固有尺寸 200px)
  → 父容器(350px,适应子元素)

双向传递:很多布局系统需要双向传递信息。第一遍自下而上收集固有尺寸,第二遍自上而下分配空间和计算位置。

布局更新可以是局部的:只重新计算受影响的子树,未改变的部分复用之前的计算结果。

小结

布局的最终结果是变换矩阵。布局方式分为显式指定(直接指定变换或位置)和约束布局(通过约束规则声明空间关系)。布局计算将布局方式转换为变换矩阵,对于约束布局,计算方向可以是自上而下、自下而上、或双向传递。当内容或约束改变时,约束布局会自动重新计算变换矩阵。

渲染单元的相互影响

复杂画面由多个渲染单元组合而成。单个渲染单元定义了"在哪里画、画什么、如何画",但渲染单元之间存在影响关系:某个渲染单元的位置可能在另一个渲染单元定义的坐标空间中表达,某个渲染效果可能作用于一组渲染单元的整体,某个渲染效果可能依赖其他渲染单元的内容。

渲染模型通过层级结构组织渲染单元,表达这些影响关系。层级结构定义了两个核心机制:坐标空间的嵌套(子节点在父节点的局部坐标系中定义)、效果的作用域(某些效果作用于父节点的所有后代)。不同渲染效果作用于层级的不同部分,形成不同的影响范围。

层级结构

渲染单元通过树形结构组织。树的父子关系定义"包含":父节点包含子节点,将子节点视为一个组。

树结构解决两个问题:

局部坐标系

子节点的位置在父节点的局部坐标系中定义。父节点的变换创建一个新的坐标系,子节点在这个坐标系中表达位置。

子节点的世界坐标 = 父节点变换 × 子节点局部坐标

这使得"移动一组元素"变得简单:只需改变父节点的变换,所有子节点自动跟随。

整体效果

某些渲染效果需要作用于"一组元素的整体"。例如对一组元素整体应用半透明:先将这组元素合成为一个整体,然后对整体应用透明度。

树结构定义了"组"的边界:父节点的某些效果(变换、透明度、模糊)作用于其所有后代,将后代作为一个整体处理。

渲染效果的影响范围

不同渲染效果作用于树的不同部分,决定了它们"影响谁"。影响范围分为三个层级:

后代层级:影响所有子孙节点

这类效果作用于节点及其所有后代,将后代视为一个整体。

效果影响机制实现方式
变换(Transform)累积继承子节点世界坐标 = 父变换 × 子局部坐标,矩阵递归相乘
透明度(Opacity)合成层将所有后代合成到离屏纹理,然后对纹理整体应用透明度
图层模糊(Layer Blur)整体模糊将整个子树渲染到离屏纹理,对纹理应用模糊,子节点之间相互模糊

兄弟层级:子节点之间相互作用

这类效果在同级节点之间产生依赖关系。

效果影响范围作用方式
蒙版(Mask)后续兄弟节点节点 A 作为蒙版,裁剪节点 B、C、D,直到容器边界或下一个蒙版
路径布尔运算(Path Boolean)参与运算的子节点收集多个子节点的 Shape,计算布尔运算(并集、交集、差集),生成新几何

背景层级:依赖已渲染内容

这类效果依赖渲染顺序,需要读取"当前节点之前渲染的内容"。

效果依赖内容实现方式
背景模糊(Backdrop Blur)节点背后的所有内容捕获截至当前的渲染结果(父背景、之前的兄弟节点),对其应用模糊
混合模式(Blend Mode)背景像素读取背景像素,与当前节点像素执行混合函数 f(src, dst) → result

合成顺序

合成顺序定义了渲染效果的执行顺序。单个渲染单元可能包含多种渲染效果(Fill、Stroke、阴影、模糊、透明度等),这些效果必须按照特定顺序执行才能产生正确的视觉结果。合成顺序的本质是"渲染管线":每个阶段处理特定的效果,阶段之间有明确的依赖关系。

为什么需要合成顺序

问题:效果的顺序影响结果

假设一个渲染单元包含 Fill 和 Drop Shadow,两种执行顺序会产生不同结果:

  • 先 Fill 后 Shadow:阴影在 Fill 下方,符合物理直觉
  • 先 Shadow 后 Fill:Fill 覆盖阴影,阴影不可见

正确的视觉效果要求固定的执行顺序。

再如透明度和模糊的顺序:

  • 先模糊后透明度:模糊作用于完整图像,然后整体变透明
  • 先透明度后模糊:透明图像的模糊会扩散 Alpha,改变轮廓

不同顺序产生完全不同的视觉效果。

依赖关系

某些效果依赖其他效果的结果:

  • 背景模糊依赖背景内容:必须先渲染背景,才能对其模糊
  • 内阴影依赖 Fill:需要 Fill 的轮廓作为裁剪边界
  • 混合模式依赖背景:需要背景像素才能执行混合函数

这些依赖关系决定了效果的执行顺序不能任意调整。

典型的合成顺序

2D 渲染系统通常采用以下合成顺序:

1. 外阴影 (Drop Shadow)
2. 背景模糊 (Background Blur)
3. 填充 (Fill)
4. 子节点 (Children)
5. 描边 (Stroke)
6. 内阴影 (Inner Shadow)
7. 图层模糊 (Layer Blur)
8. 透明度 (Opacity)
9. 混合模式 (Blend Mode)

顺序的逻辑

这个顺序遵循以下逻辑:

阶段 1:背景相关效果(外阴影、背景模糊)

这些效果需要在内容绘制之前准备背景。外阴影绘制在最底层,背景模糊捕获并处理背景。

阶段 2:内容绘制(Fill、Children、Stroke)

这是渲染单元的核心内容。Fill 和 Stroke 定义几何和样式,Children 递归渲染子节点。

Fill 通常在 Children 之前,使得子节点可以覆盖父节点的背景。Stroke 在 Children 之后,确保边框不被子节点遮挡。

阶段 3:内部效果(内阴影)

内阴影依赖 Fill 和 Children 的渲染结果,使用其轮廓作为边界。

阶段 4:整体后处理(图层模糊、透明度)

图层模糊作用于整个渲染单元(Fill + Children + Stroke + 内阴影),产生统一的模糊效果。

透明度调制整个渲染单元的 Alpha 通道,是最后的可见性控制。

阶段 5:合成(混合模式)

混合模式定义渲染单元如何与背景合成,是渲染管线的最后一步。

顺序的灵活性

虽然典型顺序是固定的,但某些情况下顺序可以调整:

Stroke 的位置

Stroke 的位置取决于是否需要被 Children 遮挡:

  • Stroke 在 Children 之后:边框在最上层,不被子节点遮挡(常见于容器边框)
  • Stroke 在 Children 之前:子节点可以覆盖边框(常见于图形绘制)

效果的嵌套

某些效果可以多次应用。例如,一个节点可以有多个阴影(不同颜色、偏移、模糊半径)。多个阴影按照定义顺序依次应用。

小结

合成顺序是渲染管线的执行顺序,确保渲染效果按照正确的依赖关系执行。典型顺序是:背景相关效果 → 内容绘制 → 内部效果 → 整体后处理 → 合成。顺序的选择基于效果之间的依赖关系和视觉逻辑。

增量更新机制

增量更新解决渲染模型变化时的性能问题。重新渲染整个场景成本高昂,增量更新只重新计算变化部分,复用未变化的渲染结果。增量更新的核心是:标记变化(哪些节点改变了)、计算影响范围(变化影响了哪些渲染结果)、局部更新(只重新渲染受影响的部分)。

为什么需要增量更新

问题:全量更新的成本

一个复杂场景包含数千个渲染单元。用户修改一个节点的颜色,如果重新渲染整个场景:

  • 重新计算数千个节点的 Geometry
  • 重新生成数千个渲染指令
  • 重绘整个屏幕

但实际上只有一个节点的颜色改变了,其他 99.9% 的内容没有变化。全量更新浪费了大量计算。

增量更新的目标

增量更新的目标是:

  1. 最小化重新计算:只重新计算变化的节点
  2. 最小化重新渲染:只重绘变化的屏幕区域
  3. 复用不变的结果:缓存未变化节点的渲染结果

变化的标记

Dirty 标记

Dirty 标记记录节点的变化类型。不同的变化类型需要不同的更新策略。

常见的 Dirty 类型:

  • 属性变化(Property Changed):节点的属性改变(颜色、尺寸、透明度等)
  • 几何变化(Geometry Changed):节点的 Shape 改变
  • 子节点变化(Children Changed):子节点增删或顺序改变
  • 效果变化(Effect Changed):渲染效果改变(阴影、模糊等)

标记传播

节点的变化会影响其他节点,Dirty 标记需要传播:

  • 向上传播:子节点变化会影响父节点的渲染结果(如父节点有图层模糊)
  • 向下传播:父节点变化会影响子节点(如父节点的变换改变)
  • 兄弟传播:节点变化会影响兄弟节点(如蒙版节点改变影响被蒙版的节点)
  • 依赖传播:节点变化会影响依赖它的节点(如背景改变影响背景模糊节点)

标记传播确保所有受影响的节点都被标记为 Dirty。

影响范围的计算

局部影响 vs 全局影响

不同的变化有不同的影响范围:

局部影响

  • 改变 Fill 颜色:只影响当前节点
  • 改变子节点顺序:只影响父节点的 Children 渲染

全局影响

  • 改变变换:影响当前节点及所有子孙节点
  • 改变蒙版:影响当前节点及被蒙版的兄弟节点
  • 改变背景:影响所有依赖背景的节点(背景模糊、混合模式)

计算影响范围决定了哪些节点需要重新渲染。

依赖追踪

对于依赖关系(如背景模糊依赖背景节点),系统需要维护依赖图:

背景节点 A[依赖 A 的背景模糊节点列表]

当 A 改变时,查找依赖图,标记所有依赖节点为 Dirty。

局部更新策略

渲染结果缓存

未变化的节点可以缓存其渲染结果(像素数据或渲染指令)。标记为 Dirty 的节点重新渲染,未标记的节点直接复用缓存。

脏矩形(Dirty Rectangle)

只重绘屏幕上变化的区域。每个 Dirty 节点贡献一个脏矩形(节点的边界框),所有脏矩形的并集是需要重绘的区域。

差异化更新

根据 Dirty 类型选择更新策略:

  • 颜色改变:只重新生成 Paint,复用 Geometry
  • 几何改变:重新计算 Geometry,可能复用 Paint
  • 子节点改变:Diff 子节点列表,只更新增删的子节点

差异化更新进一步减少计算量。

增量更新的限制

某些变化无法增量

某些变化必须全量更新:

  • 根节点改变:影响整个场景
  • 全局效果改变:如色彩空间转换、全局滤镜
  • 渲染后端改变:从 CPU 渲染切换到 GPU 渲染

这些情况下,增量更新退化为全量更新。

缓存的内存开销

缓存渲染结果需要内存。对于大型场景,缓存所有节点的渲染结果会占用大量内存。系统需要平衡内存占用和更新性能,使用 LRU 等策略管理缓存。

小结

增量更新通过标记变化、计算影响范围、局部更新三个步骤,最小化重新计算和重绘。Dirty 标记记录变化类型,标记传播确保所有受影响节点被标记,局部更新策略只更新变化部分。增量更新是复杂场景保持高性能的关键。

动态渲染模型

动态渲染模型在静态基础上引入交互和时间演化。事件系统捕获用户输入,驱动两种动态行为:滑动和动画。滑动响应拖拽事件,改变渲染单元的空间偏移。动画在时间轴上插值渲染单元的属性,产生平滑的视觉变化。动态渲染模型使得静态画面变为可交互的动态体验。

滑动

滑动是响应拖拽事件的空间偏移行为。用户拖拽滚动容器时,容器内的内容产生偏移,超出容器边界的部分被裁剪。滑动的本质是动态改变渲染单元的偏移量,不改变渲染单元的内容。

滑动的模型

滚动容器(Scroll Container)

滑动发生在滚动容器中。滚动容器定义:

  • 视口(Viewport):容器的可见区域
  • 内容区域(Content):容器内所有子节点的总边界
  • 滚动偏移(Scroll Offset):内容相对视口的偏移量
视口:(0, 0, 300, 400) - 屏幕上可见的区域
内容:(0, 0, 300, 1000) - 实际内容的大小
滚动偏移:(0, -200) - 内容向上偏移 200 像素

滚动方向

滚动容器支持两个方向的滚动:

  • 垂直滚动:内容高度超出视口高度
  • 水平滚动:内容宽度超出视口宽度
  • 双向滚动:内容在两个方向都超出视口

滚动方向决定了拖拽事件如何映射为滚动偏移。

滚动的实现

动态偏移

滑动通过动态偏移实现,不重新生成渲染单元。滚动容器的子节点应用一个动态变换:

子节点的渲染位置 = 子节点的原始位置 + 滚动偏移

动态偏移使得滑动可以高性能执行:每帧只更新偏移值,不重新计算 Geometry 和 Paint。

裁剪边界

滚动容器使用 Clip 裁剪超出视口的内容。裁剪边界是视口的边界矩形,确保只有视口内的内容可见。

滚动范围限制

滚动偏移不能无限制,受内容和视口大小限制:

最小偏移 = 0(内容顶部对齐视口顶部)
最大偏移 = 内容大小 - 视口大小(内容底部对齐视口底部)

当用户拖拽超出滚动范围时,系统可以选择:

  • 硬边界:偏移固定在边界值,不允许超出
  • 弹性边界:允许短暂超出,松手后弹回边界

惯性滚动

物理模拟

用户快速拖拽后松手,内容应该继续滚动并逐渐减速,这是惯性滚动。惯性滚动模拟物理运动:

速度 = 拖拽结束时的速度
每帧:
  速度 = 速度 × 阻尼系数(如 0.95)
  偏移 = 偏移 + 速度
  if 速度接近 0:停止滚动

阻尼系数控制减速快慢,系数越小减速越快。

边界弹性

当惯性滚动超出边界时,应用更强的阻尼并反向弹回:

if 偏移 > 最大偏移:
  超出距离 = 偏移 - 最大偏移
  阻尼 = 1 / (1 + 超出距离 / 内容大小)
  偏移 = 最大偏移 + 超出距离 × 阻尼

超出越远,阻尼越强,形成"橡皮筋"效果。

固定定位与粘性定位

固定定位(Fixed Positioning)

固定定位的节点不随滚动偏移移动,始终固定在视口的特定位置。固定定位节点的渲染位置计算:

渲染位置 = 视口位置 + 固定偏移(忽略滚动偏移)

固定定位常用于导航栏、悬浮按钮等始终可见的 UI 元素。

粘性定位(Sticky Positioning)

粘性定位的节点在滚动到特定位置后固定。粘性定位定义:

  • 粘性范围:节点可以粘性固定的滚动范围
  • 粘性偏移:节点固定时相对视口的位置
if 滚动偏移 in 粘性范围:
  渲染位置 = 视口位置 + 粘性偏移(固定)
else:
  渲染位置 = 原始位置 + 滚动偏移(正常滚动)

粘性定位常用于表格标题、分组标题等需要"吸顶"的元素。

小结

滑动通过动态偏移实现内容的空间移动,不改变渲染单元的内容。滚动容器定义视口、内容和偏移的关系,裁剪边界确保只有视口内可见。惯性滚动通过物理模拟产生自然的减速效果。固定定位和粘性定位扩展了滚动行为,使得部分节点不受或部分受滚动影响。

动画

动画是渲染属性随时间的变化。渲染属性包括渲染单元的属性(Geometry、Paint)和渲染效果(透明度、变换、模糊等)。动画的生成方式分为两类:状态插值(定义起始和结束状态,在两者之间插值)和程序生成(根据算法计算每帧的值,如物理模拟、数学函数等)。动画的核心问题是:如何生成动画、如何实现动画(可变化的渲染对象)、如何计算属性值(插值或程序生成)、如何控制进度(时间到进度的映射)。

动画生成

动画生成定义动画的输入条件。生成方式分为两类:显式定义和自动推导。

显式定义

显式定义由用户直接指定动画的参数:

  • 目标属性:哪些渲染属性需要变化
  • 变化规则:属性如何随时间变化(插值、程序生成等)
  • 时间参数:动画的时长、延迟等时间控制

显式定义的优势是精确和可控,适用于设计师制作的动画效果。

自动推导

自动推导根据状态变化生成动画。给定两个状态(如场景 A 和场景 B),系统自动计算差异并生成过渡动画。

自动推导的核心是计算"什么改变了":对比两个状态的渲染属性,找出差异的属性和值,为这些差异生成动画。

自动推导的优势是自动化,无需手动配置每个属性,适用于状态驱动的系统。

动画实现

动画实现有两种机制:属性动态变化和图层动态合成。

帧动画

帧动画是最直接的动画形式:预先准备一系列静态图像(帧),按顺序快速播放形成动画效果。每帧是完整的独立画面,不需要插值计算。

帧动画:[图像1, 图像2, 图像3, ...] → 按时间切换
渲染 = 图像序列[当前帧索引]

帧动画的本质是离散的图像序列,通过视觉暂留产生连续运动的错觉。帧动画适合复杂、不规则的运动(如角色动画、特效),但存储和传输成本高。现代动画系统多采用属性插值(更小的数据量),但帧动画在游戏精灵动画、GIF 动画中仍广泛使用。

属性动态变化

属性动态变化使得渲染属性根据进度计算,而非固定值。

静态渲染:属性 = 固定值
动态渲染:属性 = f(进度)

示例

  • 动态透明度透明度 = lerp(0, 1, 进度) - 淡入效果
  • 动态变换变换 = lerp(起始矩阵, 结束矩阵, 进度) - 平移、旋转、缩放
  • 动态颜色颜色 = lerp(红色, 蓝色, 进度) - 颜色渐变
  • 动态偏移偏移 = 初始速度 × 时间 × 阻尼^时间 - 惯性滚动

图层动态合成

图层动态合成使得多个渲染单元的组合方式根据进度变化。

静态渲染:多个渲染单元按固定方式组合
动态渲染:组合方式 = g(进度)

示例

  • 动态透明度合成:两个渲染单元,各自的透明度随进度变化,透明度A = 1 - 进度透明度B = 进度
  • 动态裁剪合成:裁剪区域随进度变化,如擦除动画、展开动画

进度控制

进度控制将时间映射为进度值(0-1)。不同的映射函数产生不同的动画节奏。

线性进度

最简单的映射是线性:

progress = (当前时间 - 起始时间) / 时长

线性进度产生匀速动画,没有加速或减速。

缓动函数(Easing Function)

缓动函数改变进度曲线,产生加速、减速、回弹等效果。缓动函数是 f: [0, 1] → [0, 1] 的映射。

常见的缓动函数:

  • Ease Inf(t) = t²,开始慢、结束快
  • Ease Outf(t) = 1 - (1 - t)²,开始快、结束慢
  • Ease In Outf(t) = 3t² - 2t³,开始慢、中间快、结束慢

贝塞尔缓动

贝塞尔缓动使用三次贝塞尔曲线定义进度曲线。给定两个控制点 (x1, y1) 和 (x2, y2),曲线从 (0, 0) 到 (1, 1)。

贝塞尔缓动的优势是灵活性:通过调整控制点可以定义任意平滑曲线。

弹簧模拟(Spring Simulation)

弹簧模拟使用物理公式计算进度。给定质量、刚度、阻尼参数,模拟弹簧的振荡运动:

加速度 = -(刚度 / 质量) × (当前位置 - 目标位置) - (阻尼 / 质量) × 速度
速度 = 速度 + 加速度 × 时间步长
位置 = 位置 + 速度 × 时间步长

弹簧模拟产生自然的回弹效果,常用于交互动画。

程序生成进度

程序生成不依赖固定的起始和结束状态,而是根据算法计算每帧的值。惯性滚动是程序生成的典型例子:

每帧:
  速度 = 速度 × 阻尼
  偏移 = 偏移 + 速度

程序生成的结束时机由算法决定(如速度接近 0),不是预定义的时长。

小结

动画通过配置或智能匹配生成,可变渲染对象支持属性的动态变化。属性插值根据不同类型使用不同算法,进度控制通过缓动函数或物理模拟映射时间到进度。动画使得静态画面在时间轴上演化,产生流畅的视觉变化。

事件系统

事件系统捕获用户输入并转换为渲染系统可识别的交互信号。事件系统的核心是三层结构:基础事件(原始输入)、事件合成(高级交互)、持续状态(交互过程的状态跟踪)。基础事件来自输入设备,事件合成将基础事件组合为语义化的交互,持续状态记录交互过程的中间状态。

基础事件

基础事件是输入设备的原始信号,直接反映用户的物理操作。

指针事件

  • Down:指针按下
  • Move:指针移动
  • Up:指针抬起

每个事件包含位置信息和时间戳。

键盘事件:KeyDown、KeyUp

其他输入:滚轮、手势等

基础事件是低级的、无语义的原始信号。

事件合成

事件合成将基础事件序列组合为高级的语义化交互。

组合逻辑

事件合成通过分析基础事件的序列、位置、时间间隔,识别用户的意图:

  • 点击(Click):Down + Up,位置接近、时间短
  • 拖拽(Drag):Down + Move(距离超过阈值) + Up
  • 双击(Double Click):两次 Click,时间间隔短
  • 长按(Long Press):Down 后持续时间长且无 Move

空间合成

空间合成基于指针位置与渲染单元边界的关系:

  • MouseEnter / MouseLeave:指针 Move 事件的位置进入或离开渲染单元的边界
  • Hover:MouseEnter 到 MouseLeave 之间的持续状态

空间合成需要命中测试:每次 Move 事件判断当前命中的渲染单元,与上一次对比,触发 Enter/Leave 事件。

合成的本质

事件合成是状态机:根据当前状态和新的基础事件,转换到新状态并可能触发高级事件。例如拖拽识别:

状态:Idle → Down → Moving(累积距离) → Dragging → Idle

持续状态

持续状态记录交互过程的状态,用于持续响应和状态查询。

状态的作用

持续状态表达"正在进行"的交互:

  • Hovering:指针正在悬停在某个渲染单元上
  • Pressing:指针正在按压某个渲染单元
  • Dragging:正在拖拽某个渲染单元

状态的生命周期

持续状态由事件触发,持续到交互结束:

进入事件 → 状态 = true
过程中:持续响应(如更新位置、显示反馈)
退出事件 → 状态 = false

状态的应用

持续状态用于:

  • 显示视觉反馈(如按钮按下状态、悬停高亮)
  • 持续更新(如拖拽时实时更新滚动偏移)
  • 状态查询(如判断"是否正在拖拽")

事件分发

事件分发将事件路由到目标渲染单元。

命中测试(Hit Test)

命中测试判断指针位置对应哪个渲染单元。从树的根节点递归遍历,找到最深的包含指针位置的节点。

事件传播

事件传播包含三个阶段:

1. 捕获阶段(Capturing):根节点 → 目标节点
2. 目标阶段:在目标节点触发
3. 冒泡阶段(Bubbling):目标节点 → 根节点

捕获阶段从根节点向下传播,父节点可以优先拦截事件(如禁用某个区域的交互)。冒泡阶段从目标节点向上传播,父节点可以响应子节点的事件(如事件委托:在列表容器统一处理所有列表项的点击)。

小结

事件系统将原始输入转换为语义化交互。基础事件捕获设备信号,事件合成通过状态机识别用户意图,持续状态记录交互过程。事件分发通过命中测试和冒泡将事件路由到目标。事件系统驱动滑动和动画,是交互能力的基础。

Figma 的渲染模型

Figma 是前两章抽象原理的具体实现。Figma 的渲染模型包含静态渲染模型和动态渲染模型,分别对应设计元素的表达和原型交互的能力。本章介绍 Figma 的文档模型:有哪些图形类型、有哪些渲染效果、如何组织层级关系、如何实现动态交互。

静态 - 渲染单元

Figma 支持多种图形类型和样式属性。

基础图形类型:

  • 矩形(Rectangle):轴对齐矩形,支持独立圆角半径
  • 椭圆(Ellipse):标准椭圆和圆形
  • 线条(Line):直线段
  • 矢量(Vector):贝塞尔曲线路径
  • 文本(Text):文字内容,支持富文本样式
  • 容器(Frame):矩形容器,可包含子元素
  • 分组(Group):逻辑分组,不可见边界

复合图形:

  • 布尔运算(Boolean Operation):路径的并集、交集、差集、排除
  • 组件(Component):可复用的设计元素
  • 实例(Instance):组件的引用实例

样式属性:

填充(Fill):纯色(RGBA)、线性渐变、径向渐变、角度渐变、图像填充。一个图形可以有多个填充,按顺序叠加。

描边(Stroke):宽度、对齐(居中/内侧/外侧)、端点样式(平直/圆形/方形)、连接样式(尖角/圆角/斜角)、虚线模式。描边使用与填充相同的颜色类型。

静态 - 渲染效果

Figma 支持多种渲染效果,扩展图形的视觉表现。

阴影:外阴影(Drop Shadow)在图形外部投射阴影,内阴影(Inner Shadow)在图形内部显示阴影。一个图形可以有多个阴影,叠加显示。

模糊:图层模糊(Layer Blur)对图形及其子元素整体模糊,背景模糊(Background Blur)对图形背后的内容模糊。

透明度与混合:透明度(Opacity)控制 0-100% 的不透明度。混合模式(Blend Mode)定义与下层内容的混合方式,支持穿透、正常、变暗类(变暗、正片叠底、颜色加深)、变亮类(变亮、滤色、颜色减淡)、对比类(叠加、柔光、强光)、差值类(差值、排除)、颜色类(色相、饱和度、颜色、明度)。

遮罩:Alpha 遮罩使用图形的 Alpha 通道裁剪后续兄弟元素,轮廓遮罩(Outline Mask)让图形本身渲染后再作为遮罩。

裁剪:容器裁剪(Clip Content)使 Frame 可以裁剪超出边界的子元素。

静态 - 层级组织及渲染影响

Figma 使用树形结构组织元素,通过 Frame 和 Group 形成层级。

层级结构:Frame 是容器,可包含子元素;Group 是逻辑分组,将多个元素组合为一个单元。层级关系定义了元素之间的渲染影响。

渲染影响:

父子影响:变换(Transform)累积到子元素,透明度(Opacity)相乘到子元素,图层模糊(Layer Blur)作用于父元素及其所有子元素。

兄弟影响:遮罩(Mask)裁剪后续的兄弟元素(直到遇到下一个遮罩或容器边界),布尔运算作用于多个兄弟元素的路径。

背景依赖:背景模糊需要元素背后已渲染的内容(父元素背景、之前的兄弟元素),混合模式与背景内容进行像素混合。

静态 - 合成顺序

Figma 按照固定的顺序合成元素的视觉效果:

1. 外阴影 (Drop Shadow)
2. 背景模糊 (Background Blur)
3. 填充 (Fill)
4. 子元素 (Children)
5. 描边 (Stroke)
6. 内阴影 (Inner Shadow)
7. 图层模糊 (Layer Blur)
8. 透明度和混合模式 (Opacity & Blend Mode)

这个顺序确保效果之间的依赖关系正确。描边的位置可以调整:如果 Frame 的 Clip Content 为 false,描边在子元素之前渲染。

静态 - 约束布局

Figma 的 Auto Layout 是约束布局的实现,根据约束自动计算元素的变换。

布局约束:

  • 排列方向:水平(Horizontal)或垂直(Vertical)排列子元素
  • 对齐方式:子元素在交叉轴上的对齐(顶部/底部/左侧/右侧/居中)
  • 间距:子元素之间的固定间距
  • 内边距:容器边缘与子元素之间的距离

尺寸模式:

  • Hug Contents:容器尺寸适应子元素(自下而上)
  • Fixed:容器尺寸固定
  • Fill Container:元素填充父容器分配的空间(自上而下)

约束(Constraints):

Constraints 是相对定位的另一种形式,定义元素相对父容器的固定关系:

  • 固定边缘:元素的某条边缘相对父容器边缘固定距离(如"距离顶部 20px")
  • 比例缩放:元素随父容器按比例缩放
  • 居中:元素在父容器中保持居中

Constraints 与 Auto Layout 可以共存:Auto Layout 作用于容器的直接子元素,Constraints 作用于非 Auto Layout 容器中的元素。

动态(原型) - 滑动

Figma 的 Frame 可以作为滚动容器。

滚动容器:支持水平、垂直、双向滚动,滚动边界由内容大小和视口大小决定,溢出行为可以是滚动(Scroll)或隐藏(Hidden)。

定位模式:固定定位(Fixed)不随滚动移动,粘性定位(Sticky)滚动到特定位置后固定。

惯性滚动:原型模式下,滑动手势触发惯性滚动,模拟物理减速效果。

动态(原型) - 动画

Figma 通过智能动画实现原型的过渡效果。

智能动画(Smart Animate)自动生成两个画面之间的过渡:根据图层名称、ID 匹配对应元素,对比属性差异,为可插值的属性生成平滑过渡,不可插值的使用溶解(Dissolve)。

可动画的属性:变换(位置、旋转、缩放)、透明度、颜色(填充和描边)、尺寸(宽度和高度)、圆角半径、滚动偏移。

溶解过渡:当元素类型改变或属性不可插值时,旧元素淡出、新元素淡入。

缓动控制:支持线性(匀速)、缓入、缓出、缓入缓出、弹簧(物理弹簧效果)。

动作类型:导航(Navigate)跳转到另一个画面,打开弹窗(Open Overlay)和关闭弹窗(Close Overlay)控制浮层,返回(Back)返回上一个画面,滚动到(Scroll To)滚动到指定位置,切换(Toggle)切换组件变体。

动态(原型) - 事件

Figma 原型支持交互触发器、持续状态和事件合成。

触发器:点击(On Click)响应鼠标点击或触摸,悬停(On Hover)响应鼠标悬停,按下(On Press)响应鼠标或触摸按下,拖拽(On Drag)响应拖拽手势,键盘(On Key)响应按键。

持续状态:原型执行时维护悬停状态(鼠标悬停在元素上)、按压状态(正在按压元素)、拖拽状态(正在拖拽元素),用于显示交互反馈。

事件合成:基础的指针事件(Down、Move、Up)合成为高级交互(Click、Drag、Hover),基础的键盘事件合成为按键操作。

小结

Figma 实现了完整的 2D 渲染模型。静态渲染模型提供丰富的图形类型(矩形、椭圆、矢量、文本等)和渲染效果(阴影、模糊、混合、遮罩),通过树形结构组织元素,按固定顺序合成视觉效果。动态渲染模型支持滚动容器、智能动画和事件触发,使得静态设计可以转化为交互原型。Figma 的渲染模型是前两章抽象原理的完整实现。

HTML/CSS 的渲染模型

CSS 是 Web 平台的样式语言,定义了 HTML 元素的视觉呈现。CSS 的渲染模型包含静态渲染模型和动态渲染模型,分别对应元素的样式表达和交互动画能力。本章介绍 CSS 如何实现 2D 渲染抽象:有哪些图形能力、有哪些渲染效果、如何组织层级关系、如何实现动态交互。

静态 - 渲染单元

CSS 中的渲染单元是 HTML 元素(Element)。每个元素通过 CSS 属性定义其几何和样式。

基础图形类型:

CSS 不直接提供图形类型,而是通过盒模型(Box Model)定义元素的矩形区域。所有元素默认是矩形,通过 CSS 属性可以变换为其他形状:

  • 矩形:元素的默认形状,由 width、height 定义
  • 圆角矩形:通过 border-radius 实现
  • 圆形/椭圆:border-radius 设置为 50%
  • 任意形状:通过 clip-path 裁剪为多边形、圆形、椭圆、路径

背景填充:

  • background-color:纯色填充
  • background-image:图像填充,支持 url() 和渐变
  • 渐变:linear-gradient(线性)、radial-gradient(径向)、conic-gradient(圆锥)

边框:

  • border:边框样式,包含 border-width、border-style、border-color
  • border-radius:圆角半径,支持每个角独立设置

文本:

  • font:字体属性,包含 font-family、font-size、font-weight、font-style
  • color:文本颜色
  • text-decoration:文本装饰(下划线、删除线等)

矢量图形 (SVG):

SVG (Scalable Vector Graphics) 是 CSS 渲染模型中的矢量表达方式。SVG 元素可以嵌入 HTML 中,通过 SVG 标签定义矢量图形。

SVG 提供完整的矢量图形能力:

  • 基础图形<rect>(矩形)、<circle>(圆形)、<ellipse>(椭圆)、<line>(直线)、<polyline>(折线)、<polygon>(多边形)
  • 路径<path> 使用路径命令(M、L、C、Q、A 等)定义任意形状,支持贝塞尔曲线
  • 文本<text> 定义矢量文本
  • 分组<g> 将多个图形分组

SVG 的样式控制:

  • fill:填充颜色或渐变
  • stroke:描边颜色、宽度、端点样式、连接样式
  • 渐变<linearGradient><radialGradient> 定义渐变填充
  • 变换:transform 属性支持平移、旋转、缩放

SVG 与 CSS 的结合:

  • SVG 元素可以应用 CSS 样式(如 opacity、filter、clip-path)
  • CSS 的渲染效果(阴影、模糊、混合模式)同样作用于 SVG 元素
  • SVG 的坐标系统独立于 HTML 盒模型,通过 viewBox 定义

静态 - 渲染效果

CSS 提供丰富的渲染效果,对应前文的可见性控制、混合模式、后处理效果。

可见性控制:

  • opacity:透明度(0-1),创建层叠上下文
  • visibility:可见性(visible/hidden),hidden 时不响应事件

裁剪:

  • clip-path:使用几何路径裁剪元素(circle、ellipse、polygon、path)
  • overflow:容器溢出处理(visible/hidden/scroll/auto)

遮罩:

  • mask-image:遮罩图像
  • mask-mode:遮罩模式(alpha/luminance)
  • mask-composite:多个遮罩层的合成方式(add/subtract/intersect/exclude)

混合模式:

  • mix-blend-mode:元素与下层内容的混合(multiply、screen、overlay 等)
  • background-blend-mode:元素多个背景层之间的混合

后处理效果:

  • filter:图像滤镜,包含:
    • 模糊:blur()
    • 颜色调整:brightness()、contrast()、saturate()、grayscale()、hue-rotate()
    • 其他:invert()、opacity()、drop-shadow()
  • backdrop-filter:背景模糊,对元素背后的内容应用滤镜

阴影:

  • box-shadow:盒阴影,支持多个阴影叠加
  • text-shadow:文本阴影

静态 - 约束布局

CSS 提供多种布局系统,自动计算元素的变换。

布局模式:

  • 正常流(Normal Flow):块级元素垂直排列,行内元素水平排列
  • Flexbox:一维弹性布局,通过 display: flex 启用
  • Grid:二维网格布局,通过 display: grid 启用
  • 定位(Positioning):通过 position 属性改变元素的定位方式

Flexbox 布局约束:

  • flex-direction:排列方向(row/column)
  • justify-content:主轴对齐方式
  • align-items:交叉轴对齐方式
  • gap:子元素间距
  • flex:子元素的伸缩比例(flex-grow、flex-shrink、flex-basis)

Grid 布局约束:

  • grid-template-rows/columns:显式定义网格轨道
  • grid-auto-rows/columns:隐式网格轨道的尺寸
  • gap:网格间距
  • justify-items/align-items:单元格内元素的对齐
  • grid-column/row:元素在网格中的位置

盒模型:

  • box-sizing:盒模型计算方式(content-box/border-box)
  • width/height:元素尺寸
  • margin:外边距
  • padding:内边距

内容驱动的尺寸:

  • min-content/max-content:元素尺寸由内容决定
  • fit-content:元素尺寸在 min-content 和 max-content 之间自适应
  • auto:尺寸由布局系统自动计算

静态 - 层级组织及渲染影响

CSS 通过 DOM 树组织元素,HTML 元素形成父子层级关系。

层级结构:DOM 树是 HTML 文档的树形结构,每个 HTML 元素(<div><span> 等)是树的节点。CSS 通过选择器匹配元素,应用样式。

渲染影响:

后代级别:

  • transform:变换累积到子元素
  • opacity:透明度相乘到子元素
  • filter:作用于元素及其所有子元素

兄弟影响:

CSS 没有直接的兄弟影响机制(如蒙版),但通过层叠顺序(z-index)和定位可以实现元素间的覆盖关系。

背景依赖:

  • backdrop-filter:依赖元素背后已渲染的内容
  • mix-blend-mode:与背景内容进行像素混合

静态 - 合成顺序

CSS 的合成顺序由两个机制决定:单个元素内部的渲染顺序、元素之间的层叠顺序。

单个元素的渲染顺序

1. backdrop-filter(对背后内容应用滤镜)
2. 绘制元素(background, border, content3. filter — 对元素自身应用滤镜
4. clip-path — 裁剪
5. mask — 遮罩
6. opacity — 透明度
7. mix-blend-mode — 与下层内容混合

元素之间的层叠顺序

层叠上下文(Stacking Context)决定多个元素的渲染顺序。某些 CSS 属性会创建新的层叠上下文:

  • opacity < 1
  • transform、filter
  • position(relative/absolute/fixed)+ z-index
  • isolation: isolate

层叠上下文内的元素按 z-index 排序。不同层叠上下文之间,按上下文的 z-index 排序。层叠上下文定义了"渲染组":内部元素先整体渲染,然后作为一个单元参与外部的层叠排序。

合成顺序的确定

1. 按层叠上下文的层级关系,确定元素的渲染顺序
2. 在同一层叠上下文内,按 z-index(或 DOM 顺序)确定渲染顺序
3. 每个元素按照单元素渲染顺序执行效果

动态 - 滑动

CSS 通过 overflow 属性支持滚动容器。

滚动容器:overflow 设置为 scroll 或 auto 时,元素成为滚动容器。内容超出容器边界时,出现滚动条,用户可以滚动查看隐藏内容。

定位模式:

  • position: fixed:固定定位,不随滚动移动,相对视口定位
  • position: sticky:粘性定位,滚动到特定位置后固定

滚动行为:

  • scroll-behavior:滚动平滑性(auto/smooth)
  • overscroll-behavior:滚动边界行为(auto/contain/none)

动态 - 动画

CSS 提供两种动画机制:过渡(Transition)和动画(Animation)。

过渡(Transition):

当 CSS 属性值改变时,过渡自动生成从旧值到新值的平滑动画。

  • transition-property:要过渡的属性
  • transition-duration:过渡时长
  • transition-timing-function:缓动函数(ease、linear、cubic-bezier)
  • transition-delay:延迟时间

动画(Animation):

通过 @keyframes 定义关键帧,元素按时间轴播放动画。

  • animation-name:动画名称,对应 @keyframes
  • animation-duration:动画时长
  • animation-timing-function:缓动函数
  • animation-iteration-count:重复次数(数字/infinite)
  • animation-direction:播放方向(normal/reverse/alternate)

可动画的属性:变换(transform)、透明度(opacity)、颜色(color、background-color)、尺寸(width、height)、位置(top、left)等。

变换(Transform):

  • transform:变换函数(translate、rotate、scale、skew)
  • transform-origin:变换原点

动态 - 事件

HTML5 提供完整的事件系统,通过 JavaScript 处理用户交互。事件系统包括基础事件、事件合成、事件分发。

基础事件:

鼠标事件

  • mousedown:鼠标按下
  • mouseup:鼠标抬起
  • mousemove:鼠标移动
  • mouseenter:鼠标进入元素(不冒泡)
  • mouseleave:鼠标离开元素(不冒泡)
  • mouseover:鼠标进入元素或其子元素(冒泡)
  • mouseout:鼠标离开元素或其子元素(冒泡)

触摸事件

  • touchstart:触摸开始
  • touchmove:触摸移动
  • touchend:触摸结束
  • touchcancel:触摸取消

指针事件(Pointer Events)

  • pointerdown/pointerup/pointermove:统一的指针事件,兼容鼠标和触摸
  • pointerenter/pointerleave:指针进入/离开元素

键盘事件

  • keydown:按键按下
  • keyup:按键抬起

滚轮事件

  • wheel:鼠标滚轮滚动

事件合成:

高级事件由基础事件组合而成:

  • click:mousedown + mouseup(位置接近、时间短)
  • dblclick:两次 click(时间间隔短)
  • contextmenu:右键点击
  • drag 系列:dragstart、drag、dragend、dragenter、dragleave、drop

事件分发:

事件冒泡(Bubbling):事件从目标元素向上传播到父元素,直到根节点。

事件捕获(Capturing):事件从根节点向下传播到目标元素。

完整的事件流程:捕获阶段 → 目标阶段 → 冒泡阶段。通过 addEventListener 的第三个参数可以选择在捕获阶段或冒泡阶段监听事件。

事件委托:利用事件冒泡,在父元素上监听子元素的事件,避免为每个子元素单独绑定事件。

小结

CSS 实现了完整的 2D 渲染模型。静态渲染模型通过盒模型和 CSS 属性定义元素的几何和样式,提供丰富的渲染效果(阴影、模糊、混合、遮罩),通过 DOM 树组织元素,按固定顺序合成视觉效果。动态渲染模型支持滚动容器、过渡动画、关键帧动画,通过伪类响应交互状态。CSS 的布局系统(Flexbox、Grid)提供强大的约束布局能力,支持响应式和内容驱动的动态响应。

SVG 的渲染模型

SVG (Scalable Vector Graphics) 是基于 XML 的矢量图形标记语言,定义了完整的 2D 矢量图形系统。SVG 可以独立使用,也可以嵌入 HTML 中。SVG 的渲染模型专注于矢量图形的精确表达,提供丰富的图形元素、样式系统和动画能力。本章介绍 SVG 如何实现 2D 渲染抽象:有哪些图形元素、渲染效果、以及动画能力。

静态 - 渲染单元

SVG 的渲染单元是图形元素(Element)。每个元素通过属性定义其几何、样式和变换。

基础图形元素:

  • <rect>:矩形,属性:x、y、width、height、rx(圆角半径)
  • <circle>:圆形,属性:cx、cy(圆心)、r(半径)
  • <ellipse>:椭圆,属性:cx、cy(圆心)、rx、ry(长短轴半径)
  • <line>:直线,属性:x1、y1、x2、y2(起点和终点)
  • <polyline>:折线,属性:points(点坐标序列)
  • <polygon>:多边形,属性:points(点坐标序列,自动闭合)
  • <path>:路径,属性:d(路径命令)

路径系统:

<path> 是 SVG 最强大的图形元素,通过路径命令定义任意复杂形状。路径命令包括:

  • M/m:移动到(MoveTo)
  • L/l:直线到(LineTo)
  • H/h:水平线到
  • V/v:垂直线到
  • C/c:三次贝塞尔曲线
  • S/s:平滑三次贝塞尔曲线
  • Q/q:二次贝塞尔曲线
  • T/t:平滑二次贝塞尔曲线
  • A/a:椭圆弧
  • Z/z:闭合路径

大写命令使用绝对坐标,小写命令使用相对坐标。

文本元素:

  • <text>:文本,属性:x、y(位置)、font-family、font-size、fill 等
  • <tspan>:文本片段,在 <text> 内定义不同样式的文本
  • <textPath>:文本沿路径排列

填充和描边:

填充(Fill)

  • fill:填充颜色,支持纯色、渐变、图案
    • 纯色:直接指定颜色值(fill="red"fill="#ff0000"
    • 渐变:引用渐变定义(fill="url(#gradientId)"
    • 图案:引用图案定义(fill="url(#patternId)"
  • fill-opacity:填充透明度
  • fill-rule:填充规则(nonzero/evenodd)

描边(Stroke)

  • stroke:描边颜色,支持纯色、渐变、图案
  • stroke-width:描边宽度
  • stroke-opacity:描边透明度
  • stroke-linecap:线条端点样式(butt/round/square)
  • stroke-linejoin:线条连接样式(miter/round/bevel)
  • stroke-dasharray:虚线模式
  • stroke-dashoffset:虚线偏移

渐变(Gradient):

渐变是 Paint 的一种类型,定义颜色在空间中的分布。

  • <linearGradient>:线性渐变,属性:x1、y1、x2、y2(渐变方向)
  • <radialGradient>:径向渐变,属性:cx、cy(中心)、r(半径)
  • <stop>:渐变色标,属性:offset(位置 0-1)、stop-color(颜色)、stop-opacity(透明度)

渐变定义在 <defs> 中,通过 fill="url(#gradientId)"stroke="url(#gradientId)" 引用。

示例:

<defs>
  <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
    <stop offset="0%" stop-color="red"/>
    <stop offset="100%" stop-color="blue"/>
  </linearGradient>
</defs>
<rect width="200" height="100" fill="url(#grad1)"/>

图案(Pattern):

图案是 Paint 的另一种类型,使用图形元素平铺填充区域。

  • <pattern>:图案定义,属性:
    • x、y、width、height:图案单元的尺寸和位置
    • patternUnits:坐标系统(userSpaceOnUse/objectBoundingBox)
    • patternContentUnits:图案内容的坐标系统

图案定义在 <defs> 中,通过 fill="url(#patternId)" 引用。

示例:

<defs>
  <pattern id="pattern1" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
    <circle cx="10" cy="10" r="5" fill="red"/>
  </pattern>
</defs>
<rect width="200" height="100" fill="url(#pattern1)"/>

静态 - 渲染效果

SVG 提供丰富的渲染效果,主要通过滤镜系统、裁剪和遮罩实现。

裁剪和遮罩:

  • <clipPath>:裁剪路径,定义可见区域(硬边界)

    <clipPath id="clip">
      <circle cx="50" cy="50" r="40"/>
    </clipPath>
    <rect width="100" height="100" clip-path="url(#clip)"/>
    
  • <mask>:遮罩,使用像素的亮度或 Alpha 控制可见性(软边界)

    <mask id="mask">
      <rect width="100" height="100" fill="white"/>
      <circle cx="50" cy="50" r="40" fill="black"/>
    </mask>
    <rect width="100" height="100" mask="url(#mask)"/>
    

滤镜系统:

SVG 的滤镜(Filter)是强大的图像处理系统,通过组合滤镜基元(Filter Primitive)实现复杂效果。

常用滤镜基元:

  • <feGaussianBlur>:高斯模糊,属性:stdDeviation(模糊半径)
  • <feOffset>:偏移,属性:dx、dy(偏移距离)
  • <feBlend>:混合,属性:mode(混合模式)
  • <feColorMatrix>:颜色矩阵变换
  • <feComponentTransfer>:颜色通道变换
  • <feComposite>:图像合成,属性:operator(合成操作)
  • <feMorphology>:形态学操作(膨胀/腐蚀)
  • <feConvolveMatrix>:卷积矩阵
  • <feTurbulence>:噪声生成
  • <feDropShadow>:投影(快捷方式)

滤镜组合示例(投影效果):

<filter id="shadow">
  <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
  <feOffset dx="2" dy="2"/>
  <feMerge>
    <feMergeNode/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
</filter>

透明度和混合:

  • opacity:元素透明度(0-1)
  • fill-opacity / stroke-opacity:填充/描边透明度

静态 - 层级组织

SVG 通过树形结构组织元素,提供分组、定义和复用机制。

容器元素:

  • <svg>:根容器,定义画布的 viewport 和 viewBox
  • <g>:分组容器,将多个元素组合为一个单元
  • <defs>:定义容器,包含可复用的资源(渐变、图案、裁剪路径、符号等),不直接渲染
  • <symbol>:符号定义,可复用的图形模板,包含自己的 viewBox

分组(<g>):

分组将多个元素组合为一个单元,共享属性和变换:

  • 变换(transform):作用于分组内所有元素,变换累积
    <g transform="translate(50, 50) rotate(45)">
      <circle r="20"/>
      <rect width="40" height="40"/>
    </g>
    
  • 样式继承:子元素继承分组的 fill、stroke 等属性
  • 透明度(opacity):分组的 opacity 作用于整体,子元素先合成再应用透明度
  • 裁剪和遮罩:clip-path 和 mask 作用于分组内所有元素

分组可以嵌套,形成层级树。分组本身不产生渲染,只是组织和控制子元素。

复用机制:

  • <defs> + <use>:定义和引用模式

    <defs>
      <circle id="dot" r="5" fill="red"/>
    </defs>
    <use href="#dot" x="10" y="10"/>
    <use href="#dot" x="30" y="30"/>
    

    <use> 创建元素的实例,可以通过 x、y 属性偏移位置。被引用的元素可以是任何图形元素或分组。

  • <symbol> + <use>:定义可复用的图形模板

    <defs>
      <symbol id="icon" viewBox="0 0 20 20">
        <circle cx="10" cy="10" r="8"/>
        <path d="M 10,5 L 10,15 M 5,10 L 15,10"/>
      </symbol>
    </defs>
    <use href="#icon" width="50" height="50"/>
    

    <symbol> 包含自己的 viewBox,通过 <use> 引用时可以指定渲染尺寸,symbol 内容自动缩放。

复用的优势:同一个定义可以在多处引用,修改定义会影响所有引用。

静态 - 布局

SVG 的布局基于坐标系统和变换。SVG 使用多层嵌套的坐标系统,提供灵活的空间映射能力。

viewport 和 viewBox:

viewport(视口)

SVG 元素的 width 和 height 属性定义 viewport,即 SVG 在页面上占据的实际尺寸(以像素或其他 CSS 单位)。

<svg width="200" height="100">
  <!-- viewport 是 200×100 -->
</svg>

viewBox(用户坐标系统)

viewBox 定义 SVG 内容的坐标范围,格式为 min-x min-y width height。viewBox 是内容的逻辑坐标系统,与 viewport 的物理尺寸独立。

<svg width="200" height="100" viewBox="0 0 100 50">
  <!-- 内容坐标范围是 0,0 到 100,50 -->
  <!-- 会自动缩放 2 倍映射到 200×100 的 viewport -->
  <circle cx="50" cy="25" r="20"/>
</svg>

viewBox 的作用:

  • 缩放:内容自动缩放以适应 viewport
  • 平移:min-x 和 min-y 控制可见区域的起点
  • 裁剪:超出 viewBox 范围的内容不可见

preserveAspectRatio

控制 viewBox 如何映射到 viewport,当两者宽高比不同时如何处理。

格式:<align> [<meetOrSlice>]

  • align:对齐方式(xMinYMin、xMidYMid、xMaxYMax 等,或 none)
  • meetOrSlice
    • meet(默认):保持宽高比,完整显示内容(类似 contain)
    • slice:保持宽高比,填满 viewport(类似 cover)
<svg width="200" height="100" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
  <!-- 内容是正方形,viewport 是矩形 -->
  <!-- 内容会缩放到 100×100 并居中 -->
</svg>

局部坐标系统:

每个元素可以通过 transform 属性定义局部坐标系统。

transform 属性

支持多种变换函数,可以组合使用:

  • translate(x, y):平移
  • rotate(angle [cx cy]):旋转,可选择旋转中心
  • scale(sx [sy]):缩放
  • skewX(angle) / skewY(angle):倾斜
  • matrix(a b c d e f):矩阵变换
<g transform="translate(50, 50) rotate(45) scale(2)">
  <rect width="20" height="20"/>
</g>

变换累积

子元素继承父元素的变换,变换从根节点到叶节点累积:

子元素世界坐标 = 父元素变换 × 子元素相对坐标

嵌套的变换形成变换树,每个节点的最终位置是所有祖先变换的累积结果。

坐标空间

SVG 中存在多个坐标空间:

  • viewport space:页面上的物理空间
  • user space:viewBox 定义的用户坐标空间
  • local space:元素的局部坐标空间(应用 transform 后)

元素的坐标从 local space 变换到 user space,再映射到 viewport space。

静态 - 合成顺序

SVG 按照元素在 DOM 树中的顺序渲染(绘制顺序),后定义的元素在上层。没有 z-index 概念,层级顺序由 DOM 顺序决定。

渲染顺序:

  1. 填充(fill)
  2. 描边(stroke)
  3. 标记(marker)
  4. 滤镜(filter)
  5. 裁剪(clip-path)
  6. 遮罩(mask)
  7. 透明度(opacity)

这个顺序与 CSS 渲染顺序类似。

动态 - 动画

SVG 提供三种动画机制:SMIL 动画、CSS 动画、JavaScript 动画。

SMIL 动画:

SVG 内置的声明式动画系统,通过动画元素定义。

  • <animate>:属性动画

    <circle cx="50" cy="50" r="20">
      <animate attributeName="cx" from="50" to="200" dur="2s" repeatCount="indefinite"/>
    </circle>
    
  • <animateTransform>:变换动画

    <rect width="20" height="20">
      <animateTransform attributeName="transform" type="rotate"
                        from="0 10 10" to="360 10 10" dur="2s" repeatCount="indefinite"/>
    </rect>
    
  • <animateMotion>:路径动画,元素沿路径运动

    <circle cx="0" cy="0" r="5">
      <animateMotion path="M 10,10 Q 50,50 90,10" dur="3s" repeatCount="indefinite"/>
    </circle>
    
  • <set>:设置属性值(离散动画)

SMIL 动画属性:

  • attributeName:动画的目标属性
  • from/to/by:起始值/结束值/变化量
  • dur:动画时长
  • repeatCount:重复次数(数字/indefinite)
  • fill:动画结束后的行为(freeze 保持/remove 移除)
  • begin/end:动画开始/结束时间,支持事件触发(如 click)

CSS 动画:

SVG 元素可以应用 CSS 动画和过渡,与 HTML 元素相同:

circle {
  transition: cx 0.3s ease;
}
circle:hover {
  cx: 100;
}

CSS 动画的限制:某些 SVG 特有属性(如路径 d)不能通过 CSS 动画,需要 SMIL 或 JavaScript。

JavaScript 动画:

通过 JavaScript 操作 SVG DOM,实现动画:

const circle = document.querySelector('circle');
circle.setAttribute('cx', 100);

Web Animations API 也可用于 SVG 元素。

动态 - 事件

SVG 元素支持 DOM 事件,与 HTML 元素相同:

鼠标事件:

  • click/dblclick:点击事件
  • mousedown/mouseup:鼠标按下/抬起
  • mousemove:鼠标移动
  • mouseenter/mouseleave:鼠标进入/离开元素
  • mouseover/mouseout:鼠标进入/离开元素或其子元素

SVG 特有事件:

  • load:SVG 文档加载完成
  • SVGZoom:缩放事件(已废弃,使用 transform 代替)

事件绑定:

<circle cx="50" cy="50" r="20" onclick="alert('clicked')"/>

或通过 JavaScript:

circle.addEventListener('click', (e) => {
  console.log('clicked');
});

交互状态:

SVG 元素可以使用 CSS 伪类:

circle:hover {
  fill: red;
}
circle:active {
  fill: blue;
}

小结

SVG 实现了完整的矢量图形渲染模型。静态渲染模型提供丰富的图形元素(基础图形、路径、文本),强大的样式系统(填充、描边、渐变、图案),以及完整的效果系统(滤镜、裁剪、遮罩)。SVG 通过树形结构组织元素,支持分组、变换和复用。动态渲染模型提供三种动画机制(SMIL、CSS、JavaScript),支持属性动画、变换动画、路径动画。SVG 元素支持完整的 DOM 事件系统。SVG 的优势是矢量图形的精确表达和无损缩放,广泛用于图标、图表、插画等场景。

Lottie 的渲染模型

Lottie 是由 Airbnb 开发的动画库,解析 Adobe After Effects 导出的动画数据(JSON 格式),在多个平台上原生渲染。Lottie 的渲染模型专注于动画表达,将 After Effects 的动画能力映射到 2D 渲染抽象。本章介绍 Lottie 如何实现 2D 渲染抽象:支持哪些图形类型、渲染效果、以及动画能力。

参考资源

静态 - 渲染单元

Lottie 中的渲染单元是图层(Layer)。每个图层包含形状(Shape)、变换(Transform)、样式等属性。

图层类型:

  • 形状图层(Shape Layer):包含矢量形状,支持 Fill 和 Stroke
  • 图像图层(Image Layer):显示位图图像
  • 固态图层(Solid Layer):纯色矩形
  • 预合成图层(Precomp Layer):引用另一个合成作为子图层
  • 空对象图层(Null Layer):不可见,用于组织层级和控制变换
  • 文本图层(Text Layer):文本内容,支持字体、样式、动画

形状类型:

  • Shape:自定义路径,通过贝塞尔曲线定义
  • Rectangle:矩形
  • Rounded Rectangle:圆角矩形
  • Ellipse:椭圆和圆形
  • Polystar:多边形和星形
  • Group:形状分组

路径运算(Merge Paths):

对多个形状路径进行布尔运算,生成新的路径几何(Android KitKat+ 和 Windows 支持):

  • Merge:合并路径
  • Add:并集
  • Subtract:差集
  • Intersect:交集
  • Exclude Intersection:异或

路径运算在生成 Geometry 阶段执行,将多个 Shape 合并为一个新的 Shape,然后应用 Fill/Stroke。

填充(Fill):

  • 纯色填充:RGBA 颜色
  • 线性渐变(Linear Gradient):沿直线方向的渐变
  • 径向渐变(Radial Gradient):从中心向外辐射的渐变
  • 填充规则(Fill Rule):控制路径的填充方式(NonZero/EvenOdd)

描边(Stroke):

  • 颜色:纯色或渐变
  • 宽度:线条粗细
  • Line Cap:线条端点样式(Butt/Round/Square)
  • Line Join:线条连接样式(Miter/Round/Bevel)
  • Miter Limit:尖角限制
  • 虚线(Dashes):虚线模式

静态 - 渲染效果

Lottie 支持部分渲染效果,主要集中在图层级别的效果。

裁剪(Mask):Mask 使用路径定义可见区域,支持硬边界和软边界:

  • Mask Path:裁剪路径,定义可见区域的边界
  • Mask Opacity:控制整个裁剪区域的透明度强度,result.a = current.a × mask_opacity(全局统一调制)
  • Mask 组合:多个 Mask 可以组合,通过布尔运算定义最终的裁剪区域。
    • Add:叠加裁剪区域(多个 Mask 的并集)
    • Subtract:从前一个 Mask 中减去当前 Mask
    • Intersect:交集(部分平台支持)

遮罩(Matte):Matte 使用一个图层的像素信息逐像素调制另一个图层的透明度,支持复杂的渐变和图案效果。

  • Alpha Matte:使用 Matte 图层的 Alpha 通道逐像素控制当前图层的可见性

    result.rgba = (current.r, current.g, current.b, current.a × matte.a(x, y))
    

    本质:每个像素的透明度独立计算,Matte 图层可以有渐变、图案等复杂效果

  • Alpha Inverted Matte:反转的 Alpha Matte

    result.rgba = (current.r, current.g, current.b, current.a × (1 - matte.a(x, y)))
    
  • Luma Matte:使用 Matte 图层的亮度逐像素控制当前图层的可见性(部分平台支持)

    luminance(x, y) = 0.299 × matte.r + 0.587 × matte.g + 0.114 × matte.b
    result.rgba = (current.r, current.g, current.b, current.a × luminance(x, y))
    

Mask 和 Matte 的本质区别:Mask 是几何裁剪 + 全局透明度,Matte 是图层像素级透明度映射。

图层效果(Layer Effects):

Web 平台支持的效果(Android/iOS 部分支持):

  • Tint:色调调整。计算像素亮度,在两个指定颜色之间插值重新着色,result = lerp(暗色, 亮色, 像素亮度)
  • Tritone:三色调。使用三个颜色映射亮度,在暗色、中间色、亮色之间分段插值,创造更丰富的色彩效果
  • Gaussian Blur:高斯模糊
  • Drop Shadow:投影

形状效果:

  • Trim Path:路径修剪,通过起点、终点、偏移控制路径的可见部分
  • Repeater:形状重复器,复制形状并应用变换
  • Round Corners:圆角效果

静态 - 层级组织

Lottie 通过合成(Composition)组织图层。合成是图层的容器,定义了渲染的画布尺寸和帧率。

合成结构:

  • 根合成(Root Composition):动画的顶层合成,包含所有图层和资源
  • 图层列表(Layers):合成内的图层按索引顺序排列,后添加的图层在上层
  • 资源(Assets):可复用的资源,包括图像和预合成

父子关系(Parenting):

图层可以设置父图层,子图层继承父图层的变换。变换累积:

子图层世界变换 = 父图层变换 × 子图层相对变换

父子关系形成层级变换树,变换从根节点向叶节点累积。空对象图层(Null Layer)常用作父图层,控制一组子图层的变换。

预合成(Precomps):

预合成是嵌套的合成,作为图层嵌入父合成。预合成的渲染过程:

  1. 预合成内的所有图层按顺序渲染到离屏纹理
  2. 预合成作为一个整体(纹理)渲染到父合成
  3. 预合成图层可以应用变换、遮罩、蒙版等效果

预合成支持多层嵌套,形成合成树。预合成的优势是复用和封装:同一个预合成可以在多处引用,修改预合成影响所有引用。

静态 - 合成顺序

Lottie 按照图层顺序渲染,后添加的图层在上层。图层内的形状按照定义顺序渲染,形状效果(Fill、Stroke)按照添加顺序应用。

遮罩和蒙版在渲染图层内容后应用,裁剪最终的像素。图层效果(如模糊、阴影)作用于图层的所有内容。

动态 - 动画

Lottie 的核心是关键帧动画。所有可动画的属性都通过关键帧定义,在关键帧之间插值。

关键帧动画:

Lottie 的动画数据由关键帧序列组成。每个关键帧包含:

  • 时间(Time):关键帧在时间轴上的位置(帧数)
  • 值(Value):该时刻的属性值
  • 缓动函数(Easing):到下一个关键帧的插值曲线

可动画的属性:

变换(Transform)

  • Position:位置,支持分离的 X/Y 轴
  • Scale:缩放
  • Rotation:旋转
  • Anchor Point:锚点
  • Opacity:透明度
  • Skew:倾斜

形状属性

  • Shape Path:路径顶点位置
  • Rectangle Size:矩形尺寸
  • Ellipse Size:椭圆尺寸
  • Polystar Points:多边形/星形的点数和半径

样式属性

  • Fill Color:填充颜色
  • Fill Opacity:填充透明度
  • Stroke Color:描边颜色
  • Stroke Width:描边宽度
  • Gradient Start/End Point:渐变起点和终点

效果属性

  • Trim Path Start/End/Offset:路径修剪参数
  • Repeater Copies/Offset:重复器参数
  • Mask Path/Opacity:遮罩路径和透明度

插值类型:

  • 线性插值(Linear):匀速变化
  • 贝塞尔插值(Bezier):使用贝塞尔曲线定义缓动函数
  • 保持插值(Hold):阶跃变化,不插值
  • 空间贝塞尔插值(Spatial Bezier):位置属性的空间路径插值
  • Rove Across Time:沿路径匀速运动

时间控制:

Lottie 支持对图层的时间轴进行控制:

  • Time Stretch:拉伸或压缩图层的时间,改变播放速度。值为 2 时,图层以 2 倍速播放;值为 0.5 时,以半速播放。
  • Time Remap:重新映射图层的时间轴。Time Remap 本身可以是关键帧动画,通过控制时间轴的进度实现时间倒流、循环、暂停等效果。

Time Remap 是强大的时间控制机制:可以让图层在某段时间循环播放,或在不同时刻跳转到不同的帧。

表达式:Web 版 Lottie 对 After Effects 的表达式(Expressions)提供有限支持。表达式是 JavaScript 代码,在每帧执行,动态计算属性值。表达式可以访问其他图层的属性,实现复杂的关联动画。由于性能和安全考虑,Lottie 只支持常用表达式的子集,复杂的表达式可能需要简化或烘焙为关键帧。

表达式使得动画可以响应运行时状态,而不仅仅是播放预定义的关键帧。

文本动画:Lottie 支持文本图层的动画,包括:

基础文本属性:

  • 字体(Fonts):字体系列和样式
  • 字形(Glyphs):字符形状
  • 填充和描边:文本的填充颜色和描边样式
  • 变换(Transform):文本的位置、缩放、旋转
  • 字间距(Tracking):字符间距

高级文本动画(Web 支持):

  • Anchor Point Grouping:锚点分组
  • Text Path:文本沿路径排列
  • Per-character 3D:逐字符 3D 变换
  • Range Selector:范围选择器,选择文本的一部分应用动画
  • Expression Selector:表达式选择器,通过表达式控制文本动画

动画控制

Lottie 提供播放控制接口:

播放控制:

  • play():播放动画
  • pause():暂停动画
  • stop():停止并重置动画
  • goToAndStop(value, isFrame):跳转到指定时间或帧
  • goToAndPlay(value, isFrame):跳转并播放
  • setDirection(direction):设置播放方向(正向/反向)
  • setSpeed(speed):设置播放速度

片段播放:

  • playSegments(segments, forceFlag):播放指定片段

标记(Markers):

Lottie 支持 After Effects 的标记,标记可以命名时间点,用于同步和控制播放。

小结

Lottie 实现了以动画为核心的 2D 渲染模型。静态渲染模型支持多种图层类型(形状、图像、文本、预合成),提供矢量形状的完整表达(路径、矩形、椭圆、多边形),支持填充、描边、遮罩、蒙版等渲染效果。动态渲染模型通过关键帧动画驱动所有属性变化,支持多种插值类型,提供灵活的播放控制和表达式能力。Lottie 将 After Effects 的动画能力映射到跨平台的原生渲染,使得设计师可以直接导出动画而无需工程师手动实现。