关于 svg-animation
用于制作 SVG 图标、插画、标志和数据图形动画的设计技能。介绍 CSS、SMIL 与 JavaScript 在描边绘制、路径变形和沿路径运动中的用法,并涵盖优化及减少动态效果指南。
主要功能
- 01描边绘制效果
- 02SVG 路径变形
- 03沿路径移动
- 04渐变与滤镜动画
- 05SVG 优化与无障碍
适用场景
- 01制作图标状态切换动画
- 02绘制签名或地图动画
- 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
完成前:
- 可独立打开——没有控制台错误,可以访问内联 SVG,CDN(如有)能够加载。
- 驱动方式对应的 seek 机制可以冻结确定性帧。
- 已截取起始/中间/结束时刻的画面——符合要求,没有裁切或超出
viewBox的描边。 - 遵循
prefers-reduced-motion——限制循环/大幅运动,保留静态最终状态。 - 缓动有明确意图——有意选择
ease/GSAP 缓动,不要意外使用linear描画动画。
参考文件
references/svg-techniques.md— 完整的 dashoffset 数学原理和getTotalLength注意事项、pathLength="1"归一化、GSAP MorphSVG 与 Flubber 的选择指南及代码、图标切换变形(汉堡图标↔关闭图标)、MotionPath/offset-path 细节、SMIL 与 CSS 与 JS 的权衡,以及针对动画调整的 SVGO 配置。