首页
/ d3-ease 缓动函数完全解析:d3 中时间扭曲与动画质感的实现原理

d3-ease 缓动函数完全解析:d3 中时间扭曲与动画质感的实现原理

2026-09-04 21:29:50作者:裘旻烁

在 d3 中,缓动(easing)是一种通过"扭曲时间"来控制动画视觉运动的方法,最常用于实现 slow-in、slow-out(慢进慢出)效果,让 d3-transition 动画 更加平滑、更符合物理直觉。本文基于 d3 仓库的官方参考文档 docs/d3-ease.md 展开,完整覆盖 ease 函数契约、全部九种缓动类型(linear、poly、quad、cubic、sin、exp、circle、elastic、back、bounce)及其 In/Out/InOut 变体、exponent/amplitude/period/overshoot 等可配置参数的取值与默认值,并结合仓库中的源码导出链与文档可视化组件,讲清每一类缓动"何时选、怎么配、底层怎么算"。

缓动函数:一个把时间映射时间的函数契约

d3-ease 模块中的每个缓动类型都实现同一个 ease 方法:接收一个归一化时间 t,返回对应的"缓动后"时间

  • 输入 t 与输出 通常都在 [0, 1] 区间内,0 代表动画起点,1 代表动画终点;
  • 一个合格的缓动函数应当满足 ease(0) === 0ease(1) === 1
  • 个别类型(如 easeElastic)的缓动时间可能略微越出该区间——这正是"回弹""过冲"等动画质感的来源。

基本用法是给归一化时间直接套用某个缓动函数:

const te = d3.easeCubic(t);

自定义参数时,先在动画开始前创建缓动函数

const ease = d3.easeElastic.period(0.4);

再在动画过程中调用它

const te = ease(t);

在 d3 生态中的位置:

  • src/index.js 可以看到,d3 顶层包通过 export * from "d3-ease" 将全部缓动函数并入统一命名空间,因此 d3.easeCubic 等可以直接访问;package.json 中声明的依赖为 d3-ease: ^3.0.1(当前 d3 版本 7.9.0);
  • 在过渡动画中,缓动函数由 transition.ease(value) 指定:每一帧动画都会调用它一次,传入 [0, 1] 之间的归一化时间 t,返回缓动时间 ;不显式指定时默认为 d3.easeCubic
transition.ease(d3.easeCubic);
transition.ease() // 读取当前缓动函数,返回 d3.easeCubic
  • 文档中的交互式曲线图由 ExampleEase.vue 组件渲染:该组件用 d3.ticks(0, 1, 500) 在 [0, 1] 上取 500 个采样点,再用 Observable Plot 的 Plot.lineX(ticks, ease) 绘制每条缓动曲线,因此每类缓动的曲线形状都可以直观复现。

以下缓动类型大量参考了 Robert Penner 的经典 easing 公式体系。

基础族:easeLinear 与 easePoly

easeLinear:恒等函数

线性缓动即恒等函数:linear(t) 直接返回 t,时间完全不被扭曲。适合需要匀速运动的场景(如进度条扫过、循环滚动)。

easePoly:可配置指数的多项式缓动

easePolyeasePolyInOut 的别名。其三个变体的语义:

变体 行为 与 easePolyIn 的关系
easePolyIn t 提升到指定指数,默认指数 3(等价 easeCubicIn
easePolyOut 反向多项式缓动 等价 1 - easePolyIn(1 - t)
easePolyInOut 对称多项式缓动 t ∈ [0, 0.5] 用 In 段、t ∈ (0.5, 1] 用 Out 段,各自缩放拼接

通过 easePoly.exponent(e) 返回一个带指定指数 e 的新多项式缓动,可精确复现 linear/quad/cubic:

const linear = d3.easePoly.exponent(1);
const quad   = d3.easePoly.exponent(2);
const cubic  = d3.easePoly.exponent(3);

官方文档为指数提供了 0.5、1、1.5、2、3、4 六档对比曲线;指数越大,In 段前期越"压平"、末尾加速越猛,Out 段则相反。

easeQuad:二次缓动

easeQuadeaseQuadInOut 的别名:

  • easeQuadIn:等价 easePolyIn.exponent(2)
  • easeQuadOut:反向,等价 1 - easeQuadIn(1 - t),也等价 easePolyOut.exponent(2)
  • easeQuadInOut:对称,前半段缩放 easeQuadIn、后半段缩放 easeQuadOut,也等价 easePoly.exponent(2)

easeCubic:三次缓动(d3 的默认)

easeCubiceaseCubicInOut 的别名:

  • easeCubicIn:等价 easePolyIn.exponent(3)
  • easeCubicOut:等价 1 - easeCubicIn(1 - t),也等价 easePolyOut.exponent(3)
  • easeCubicInOut:对称拼接,也等价 easePoly.exponent(3)

由于 transition.ease() 未指定时默认就是 easeCubic,它是 d3 过渡动画中出场率最高的缓动,也是最稳妥的默认选择。

曲线族:easeSin、easeExp 与 easeCircle

三者都是 In / Out / InOut 三件套,且 Out 一律是 In 的时间反转 1 - fIn(1 - t),InOut 一律是"前半 In、后半 Out"的对称拼接。

easeSin:正弦缓动

  • easeSinIn:返回 sin(t)——以正弦曲线起势,起步柔和;
  • easeSinOut1 - easeSinIn(1 - t)
  • easeSinInOut:对称拼接。

easeExp:指数缓动

  • easeExpIn:将 2 提升到 10 × (t - 1) 次幂,即 2^(10(t-1))——起点几乎静止、终点极陡,加速感比三次缓动更强烈;
  • easeExpOut1 - easeExpIn(1 - t),常用于"快速冲入、缓缓收尾"的元素入场;
  • easeExpInOut:对称拼接。

easeCircle:圆形缓动

基于圆弧(四分之一圆的斜率变化)定义:

  • easeCircleIn:圆形缓动;
  • easeCircleOut1 - easeCircleIn(1 - t)
  • easeCircleInOut:对称拼接。

圆形缓动的拐点比正弦更"硬",接近抛物线末端行为,适合强调果断感的 UI 动效。

弹性族:easeElastic 及其两个配置参数

easeElasticeaseElasticOut 的别名,效果像橡皮筋,允许缓动值在终点处来回振荡(这正是前文所述"缓动时间可略越出 [0, 1]"的典型)。

  • easeElasticIn:振幅与周期均可配置,默认振幅 1、默认周期 0.3
  • easeElasticOut1 - easeElasticIn(1 - t),是最常用的"松手回弹"形态;
  • easeElasticInOut:前半段 In、后半段 Out 的对称形式。

两个参数方法均返回新的缓动函数:

// 指定振幅(注意约束)
const ease = d3.easeElastic.amplitude(1.2);
// 指定周期
const ease = d3.easeElastic.period(0.4);
  • easeElastic.amplitude(a):振幅 a 必须 ≥ 1,振荡偏离 [0, 1] 的程度随振幅增大而加剧;官方文档的交互式滑块范围为 1~4;
  • easeElastic.period(p):周期 p 控制振荡频率,滑块范围为 0.1~1.5,周期越小振荡越密集。

文档中为两个参数分别提供了 1.0~1.5 的振幅对比曲线,可以看到振幅越大、回弹过冲越夸张。

预期族:easeBack 与过冲参数

easeBackeaseBackInOut 的别名,属于"预期(anticipation)"类缓动——如同舞者起跳前先屈膝:

  • easeBackIn:先向反方向"蓄力"再冲出去,过冲度可配置,默认 1.70158
  • easeBackOut1 - easeBackIn(1 - t)
  • easeBackInOut:对称拼接。
const ease = d3.easeBack.overshoot(2.5); // 自定义过冲

easeBack.overshoot(s) 返回带指定过冲 s 的新 back 缓动;官方示例用 d3.ticks(0.5, 3, 6) 生成 0.5、1、1.5、…、3 共 6 档过冲对比曲线,交互式滑块范围为 0~5。过冲为 0 时退化为普通 In 行为,过冲越大,反方向的"下探"越深。

弹跳族:easeBounce

easeBounceeaseBounceOut 的别名,效果像一颗橡胶球落地反弹,为分段折线式的非对称振荡:

  • easeBounceIn:弹跳缓动,先慢后快、末端以多次衰减反弹收束;
  • easeBounceOut1 - easeBounceIn(1 - t),即"先落下、逐级弹跳衰减"的经典形态;
  • easeBounceInOut:前半段 In、后半段 Out 的对称拼接。

弹跳族没有可配置参数,是九类缓动中唯一不可调参的类型。

选型速查与在 d3-transition 中的落地

综合各类型的默认值与特性,选型可按此速查表决策:

缓动族 默认别名指向 关键参数(默认值) 典型观感
easeLinear 自身 匀速
easePoly easePolyInOut exponent(3) 可调强度的多项式
easeQuad easeQuadInOut 无(即 exponent 2) 温和的二次曲线
easeCubic easeCubicInOut 无(即 exponent 3) d3 默认,平滑通用
easeSin easeSinInOut 柔和的正弦起势
easeExp easeExpInOut 强烈的指数加减速
easeCircle easeCircleInOut 偏硬的圆弧转折
easeElastic easeElasticOut amplitude(1,须 ≥1)、period(0.3) 橡皮筋回弹
easeBack easeBackInOut overshoot(1.70158) 先蓄力后过冲
easeBounce easeBounceOut 橡胶球弹跳

在真实动画中的落地方式(详见 docs/d3-transition/timing.md):

// 全局统一缓动
d3.select(".bar")
  .transition()
  .duration(750)
  .ease(d3.easeCubic)          // 默认值,可省略
  .attr("height", newHeight);

// 每个元素使用不同缓动:easeVarying 接受工厂函数
selection.transition()
  .easeVarying((d) => d3.easePolyIn.exponent(d.exponent));

要点回顾:

  1. 缓动函数只负责"时间 → 时间"的映射,每帧被 transition.ease 调用一次,因此自定义缓动只需是纯函数即可;
  2. 所有 *In/*Out/*InOut 遵循统一构造规律:Out 是 In 的时间反转,InOut 是二者在 t = 0.5 处的对称拼接,记忆其中一半即可推导另一半;
  3. 可调参的三类——easePoly.exponenteaseElastic.amplitude/periodeaseBack.overshoot——均以"返回新函数"的方式创建变体,不修改原函数,天然支持动画前批量预创建;
  4. 曲线形状可用 ExampleEase.vue 同款方式快速验证:对任意缓动函数在 d3.ticks(0, 1, 500) 上采样绘图,即可与官方文档中的曲线对照。
登录后查看全文
热门项目推荐
相关项目推荐