mcpField
S
svg-animation

svg-animation

github.com/iart-ai设计与 UI632.7k 次安装18 周前更新
代码仓库

关于 svg-animation

用于制作 SVG 图标、插画、标志和数据图形动画的设计技能。介绍 CSS、SMIL 与 JavaScript 在描边绘制、路径变形和沿路径运动中的用法,并涵盖优化及减少动态效果指南。

主要功能

  1. 01描边绘制效果
  2. 02SVG 路径变形
  3. 03沿路径移动
  4. 04渐变与滤镜动画
  5. 05SVG 优化与无障碍

适用场景

  1. 01制作图标状态切换动画
  2. 02绘制签名或地图动画
  3. 03为标志和数据图形添加动效

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iart-ai/web-animation-skills --skill svg-animation

安装后在 Claude Code 中输入/svg-animation确认已加载。

附带文件README.mdreferences/svg-techniques.md

详细介绍

SVG Animation

清晰、轻量、可无限缩放的矢量动效——非常适合图标、插画、徽标和数据标记。SVG 有三种动画方式:CSS(声明式、简单)、SMIL(SVG 内的 <animate>)和 JS(GSAP/Web Animations,用于控制和变形)。按任务选择;下文会说明适用的技术。

适用场景

  • 图标、插画、签名、地图的描边“绘制”效果
  • 形状/路径变形和图标状态切换动画(菜单 ↔ 关闭、播放 ↔ 暂停)
  • 让元素沿路径移动(运动路径)
  • 动态渐变、滤镜(发光、位移)和动态徽标

核心技术

描边绘制效果(常用技巧)

将虚线数组设为与路径等长,并将偏移量设满(使其不可见),然后将偏移量动画到 0。

.path {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  animation: draw 1.4s ease forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }

获取长度:

  • JS(最可靠):const len = path.getTotalLength(); path.style.setProperty("--len", len);
  • 无 JS 技巧:在 <path> 上设置 pathLength="1",然后设为 stroke-dasharray: 1; stroke-dashoffset: 1;,并将其动画到 0。这会将任意路径规范化为 0–1 长度,无需测量。

将偏移量从 0 动画回 len 即可反向擦除。使用 animation-delay 错开多条路径。绘制方向取决于路径点的顺序;如果绘制方向“反了”,可在编辑器中反转路径,或将偏移量符号取反。

将一条路径变形为另一条路径

路径会逐点插值,因此朴素的变形要求两个 d 属性具有相同数量和类型的命令。有两种可靠的方法:

  • GSAP MorphSVG(从 GSAP 3.12 起免费)——自动处理不匹配的点数,并找到良好的映射:
gsap.registerPlugin(MorphSVGPlugin);
gsap.to("#start", { morphSVG: "#end", duration: 0.8, ease: "power2.inOut" });
// Convert any shape to a morph-able path:
MorphSVGPlugin.convertToPath("circle, rect, ellipse, line, polygon");
  • Flubber(小型独立库)——无需 GSAP 即可生成插值器,适用于 React/Framer Motion:
import { interpolate } from "flubber";
const interpolator = interpolate(pathA, pathB, { maxSegmentLength: 2 });
// interpolator(0) === pathA, interpolator(1) === pathB; feed t into a tween.

手动编写变形(如图标切换)时,确保两条路径的命令结构完全相同,并通过 Web Animations 或 CSS 直接为 d 设置动画(现代浏览器可通过 path("...") 为 d 设置动画)。

沿路径运动
  • GSAP MotionPath(首选——便于控制、对齐和拖动进度):
gsap.registerPlugin(MotionPathPlugin);
gsap.to("#rocket", {
  duration: 4, repeat: -1, ease: "none",
  motionPath: { path: "#track", align: "#track", autoRotate: true, alignOrigin: [0.5, 0.5] },
});

autoRotate: true 会让对象沿路径切线方向旋转;align 会使坐标相对于路径元素。

  • SMIL(无需 JS):
<path id="track" d="M10,80 C40,10 120,10 150,80" fill="none"/>
<circle r="6" fill="#3b82f6">
  <animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
    <mpath href="#track"/>
  </animateMotion>
</circle>
  • CSS offset-path(现代、声明式):offset-path: path("M10,80 C..."); animation: move 3s linear infinite;,搭配 @keyframes move { to { offset-distance: 100%; } } 和 offset-rotate: auto。
动态渐变和滤镜

渐变:为 gradientTransform 或色标偏移量设置动画。光泽扫过效果:

<linearGradient id="sheen">
  <stop offset="0%"  stop-color="#fff" stop-opacity="0"/>
  <stop offset="50%" stop-color="#fff" stop-opacity=".8"/>
  <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
  <animateTransform attributeName="gradientTransform" type="translate"
    from="-1 0" to="1 0" dur="2s" repeatCount="indefinite"/>
</linearGradient>

滤镜:为 feDisplacementMap 的 scale 设置动画,实现黏稠/摇摆效果;为 feGaussianBlur 的 stdDeviation 设置动画,实现焦点拉动效果;或者为 feColorMatrix/feFlood 设置动画,实现发光效果。滤镜的绘制开销较大——应谨慎添加动画,并尽可能优先使用 transform/opacity。

实现方式选择(快速选择)

需求使用
单一声明式绘制/淡入淡出CSS
自包含、无需 JS 包SMIL(SVG 内的 <animate*>)
协同控制、可拖动进度、与滚动联动GSAP
点数不匹配的变形GSAP MorphSVG 或 Flubber
沿路径移动并旋转GSAP MotionPath / CSS offset-path

SMIL 注意事项:IE/旧版 Edge 不支持,且历史上曾被标记为弃用;如需最大范围兼容或与滚动同步,优先使用 CSS 或 JS。在常青浏览器中,SMIL 仍适用于自包含的图标资源。

编写与优化

  • 使用 SVGO 构建/清理:保留 viewBox,删除编辑器元数据,但禁用 cleanupIds/removeViewBox,以及任何会重命名 CSS/JS/SMIL 所引用 ID 的插件。如果要单独为子路径或形状添加动画,请禁用 mergePaths 和 convertShapeToPath。
  • 将 SVG 内联到 DOM 中(不要使用 <img src>),以便 CSS/JS 可以访问其内部元素;嵌入在 <img> 中的 SVG 只能通过内部 SMIL/CSS 自行添加动画。
  • 设置明确的 viewBox,避免固定的 width/height,以便资源流畅缩放。
  • 对于描画动画,确保路径使用实际描边(fill:none; stroke:...),而非填充轮廓——dashoffset 只对描边有效。
  • 遵循 prefers-reduced-motion:限制循环/大幅运动;保留静态最终状态。

交付与验证(独立 HTML)

打包的辅助工具(scripts/):scripts/seek-shot.sh anim.html 0 1.5 3 会冻结 ?t=N 测试框架并截取每个时刻的画面;scripts/contact-sheet.sh sheet.png frame-*.png 会将它们拼成一张图,方便快速查看。详见 scripts/README.md。

对于自包含的图标/logo/描画动画,交付物是一个可直接在浏览器中打开的 HTML 文件——将 SVG 内联到标记中,用一种机制驱动动画,不需要构建步骤。对于矢量资源,一个文件是合适的选择;不要使用打包器。

输出约定:

  • 一个 .html 文件:内联 <svg>,以及 CSS @keyframes / 使用 CDN 的 GSAP <script> / SMIL <animate*>——选择一种驱动方式。
  • 包含与该驱动方式匹配的 seek 测试框架,以便将任意时刻冻结为截图。

Seek 测试框架——冻结确切时刻。 ?t=N 会跳转并暂停,让截图定格在静止帧上。使用与 SVG 动画方式匹配的机制:

<script>
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) {
    const N = parseFloat(t);
    // SMIL: pause the SVG's own clock and scrub it
    const svg = document.querySelector("svg");
    svg.pauseAnimations(); svg.setCurrentTime(N);
    // CSS @keyframes draw-on: el.style.animationDelay=(-N)+"s"; el.style.animationPlayState="paused";
    // GSAP timeline: tl.pause(); tl.seek(N);
  }
  window.__ready = true;
</script>

验证循环——渲染 → 冻结 → 截图 → 检查: 在起始/中间/结束时刻打开文件(?t=0、?t=<dur/2>、?t=<dur>),分别截图,并检查保真度(描边绘制方向正确,变形端点干净),以及异常情况(路径被 viewBox 裁切、描边因过期的 dashoffset 消失、FOUC、变形接缝处卡顿)。任何无头工具都可以:

npx playwright screenshot --wait-for-timeout=500 "file://$PWD/icon.html?t=0.7" frame-mid.png

完成前:

  1. 可独立打开——没有控制台错误,可以访问内联 SVG,CDN(如有)能够加载。
  2. 驱动方式对应的 seek 机制可以冻结确定性帧。
  3. 已截取起始/中间/结束时刻的画面——符合要求,没有裁切或超出 viewBox 的描边。
  4. 遵循 prefers-reduced-motion——限制循环/大幅运动,保留静态最终状态。
  5. 缓动有明确意图——有意选择 ease/GSAP 缓动,不要意外使用 linear 描画动画。

参考文件

  • references/svg-techniques.md — 完整的 dashoffset 数学原理和 getTotalLength 注意事项、pathLength="1" 归一化、GSAP MorphSVG 与 Flubber 的选择指南及代码、图标切换变形(汉堡图标↔关闭图标)、MotionPath/offset-path 细节、SMIL 与 CSS 与 JS 的权衡,以及针对动画调整的 SVGO 配置。

评论

支持 Markdown