模块6: 动画系统

基于 W3C Web Animations API 的图形动画

1. 概述

@antv/g 的动画系统实现了 W3C Web Animations API 标准。如果你熟悉浏览器的 element.animate(),就能直接上手。

源码位于 packages/g/src/plugins/web-animations-api/,核心类:Animation、KeyframeEffect、AnimationTimeline。

2. 基本用法

// 最简动画 - 与 DOM element.animate() 完全一致
const animation = circle.animate(
  [
    { r: 50, fill: '#1890ff' },   // 起始关键帧
    { r: 100, fill: '#f5222d' },  // 结束关键帧
  ],
  {
    duration: 1000,    // 持续时间 ms
    easing: 'ease-in-out',
    iterations: Infinity, // 循环次数
    direction: 'alternate', // 交替播放
  }
);

3. Animation 类

每次调用 .animate() 返回一个 Animation 实例:

// 播放控制
animation.play();     // 播放
animation.pause();    // 暂停
animation.reverse();  // 反向播放
animation.finish();   // 跳到结束
animation.cancel();   // 取消

// 属性
animation.currentTime  // 当前时间 (ms)
animation.playbackRate // 播放速率 (1=正常, 2=2倍速)
animation.playState    // idle | running | paused | finished
animation.pending      // 是否等待中

// 事件回调
animation.onfinish = () => { /* 完成时 */ };
animation.oncancel = () => { /* 取消时 */ };

// Promise 方式
await animation.finished; // 等待动画完成

4. KeyframeEffect 关键帧

// 多关键帧
circle.animate(
  [
    { cx: 100, opacity: 0 },     // 0%
    { cx: 300, opacity: 1 },     // 50%
    { cx: 500, opacity: 0.5 },   // 100%
  ],
  { duration: 2000, easing: 'linear' }
);

// 指定 offset(时间位置)
circle.animate(
  [
    { offset: 0,   r: 10 },
    { offset: 0.3, r: 80 },   // 30% 时
    { offset: 1,   r: 50 },
  ],
  { duration: 1500 }
);

5. 缓动函数 (Easing)

缓动说明
linear线性
ease默认缓动
ease-in慢入
ease-out慢出
ease-in-out慢入慢出
cubic-bezier(x1,y1,x2,y2)自定义贝塞尔
steps(n, start|end)阶梯函数
// 弹性缓动
circle.animate([...], {
  duration: 800,
  easing: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
});

// 阶梯动画
circle.animate([...], {
  duration: 1000,
  easing: 'steps(5, end)',
});

6. 可动画属性

以下样式属性支持动画插值:

类别属性
位置cx, cy, x, y, x1, y1, x2, y2
尺寸r, rx, ry, width, height
颜色fill, stroke, shadowColor
透明度opacity, fillOpacity, strokeOpacity
描边lineWidth, lineDashOffset
变换transform (translate/rotate/scale)
路径d (Path morph)

7. AnimationTimeline 时间轴

// 获取画布时间轴
const timeline = canvas.document.timeline;
timeline.currentTime;  // 当前时间

// 所有正在运行的动画
canvas.document.getAnimations();

8. 组合动画

// 串行:等第一个完成后启动第二个
const anim1 = circle.animate(
  [{ cx: 100 }, { cx: 400 }],
  { duration: 1000, fill: 'forwards' }
);
anim1.onfinish = () => {
  circle.animate(
    [{ cy: 100 }, { cy: 300 }],
    { duration: 1000, fill: 'forwards' }
  );
};

// 并行:同时启动多个动画
circle.animate([{ r: 50 }, { r: 100 }], { duration: 1000 });
circle.animate([{ fill: 'red' }, { fill: 'blue' }], { duration: 1000 });

9. fill 模式

说明
none动画结束后恢复初始值(默认)
forwards动画结束后保持最终值
backwards动画开始前应用初始关键帧
bothforwards + backwards