svg-animation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSVG Animation
SVG动画
Crisp, lightweight, infinitely scalable vector motion — ideal for icons, illustrations, logos, and data marks. SVG can be animated three ways: CSS (declarative, simple), SMIL ( inside the SVG), and JS (GSAP/Web Animations, for control and morphing). Choose per task; the techniques below say which.
<animate>清晰、轻量、可无限缩放的矢量动效——非常适合图标、插画、Logo和数据标记。SVG有三种动画实现方式:CSS(声明式,简单)、SMIL(在SVG内部使用标签)和JS(GSAP/Web Animations,用于精准控制和变形)。可根据任务选择合适的方式,以下技术会说明适用场景。
<animate>When to use
适用场景
- Stroke "draw-on" of icons, illustrations, signatures, maps
- Shape/path morphing and animated icon state changes (menu ↔ close, play ↔ pause)
- Moving an element along a path (motion path)
- Animated gradients, filters (glow, displacement), and animated logos
- 图标、插画、签名、地图的描边「绘制」效果
- 形状/路径变形以及图标状态切换动画(菜单↔关闭,播放↔暂停)
- 元素沿路径移动(运动路径)
- 带动画的渐变、滤镜(发光、位移)以及动画Logo
Core techniques
核心技术
Stroke draw-on (the staple)
描边绘制(核心技巧)
Draw the dash array as long as the path, offset it fully (invisible), then animate the offset to 0.
css
.path {
stroke-dasharray: var(--len);
stroke-dashoffset: var(--len);
animation: draw 1.4s ease forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }Getting the length:
- JS (most reliable):
const len = path.getTotalLength(); path.style.setProperty("--len", len); - No-JS trick: set on the
pathLength="1", then<path>and animate tostroke-dasharray: 1; stroke-dashoffset: 1;. This normalizes any path to a 0–1 length so no measurement is needed.0
Reverse (erase) by animating offset from 0 back to . Stagger multiple paths with . Direction of drawing follows the path's point order; reverse it in the editor or negate the offset sign if it draws "backwards".
lenanimation-delay将虚线数组设置为路径的长度,完全偏移(不可见),然后将偏移量动画到0。
css
.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–1的长度,无需测量。0
通过将偏移量从0动画回实现反向(擦除)效果。使用实现多条路径的交错动画。绘制方向遵循路径的点顺序;如果绘制方向相反,可在编辑器中反转路径,或反转偏移量的符号。
lenanimation-delayMorphing one path into another
路径变形
Paths interpolate point-by-point, so a naive morph requires both attributes to have the same number and type of commands. Two robust approaches:
d- GSAP MorphSVG (free as of GSAP 3.12) — handles mismatched point counts automatically and finds a good mapping:
js
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 (small standalone lib) — generates interpolators without GSAP, good with React/Framer Motion:
js
import { interpolate } from "flubber";
const interpolator = interpolate(pathA, pathB, { maxSegmentLength: 2 });
// interpolator(0) === pathA, interpolator(1) === pathB; feed t into a tween.For hand-authored morphs (icon toggles), keep both paths with identical command structure and animate directly via Web Animations or CSS ( is animatable in modern browsers via ).
ddpath("...")路径是逐点插值的,因此简单的变形要求两个路径的属性具有相同数量和类型的命令。以下两种可靠方法:
d- GSAP MorphSVG(GSAP 3.12起免费)——自动处理不匹配的点数并找到合适的映射:
js
gsap.registerPlugin(MorphSVGPlugin);
gsap.to("#start", { morphSVG: "#end", duration: 0.8, ease: "power2.inOut" });
// 将任何形状转换为可变形的路径:
MorphSVGPlugin.convertToPath("circle, rect, ellipse, line, polygon");- Flubber(小型独立库)——无需GSAP即可生成插值器,适用于React/Framer Motion:
js
import { interpolate } from "flubber";
const interpolator = interpolate(pathA, pathB, { maxSegmentLength: 2 });
// interpolator(0) === pathA, interpolator(1) === pathB; 将t值传入补间动画。对于手动编写的变形效果(图标切换),保持两个路径的命令结构完全相同,直接通过Web Animations或CSS动画属性(现代浏览器中可通过实现动画)。
ddpath("...")Motion along a path
沿路径运动
- GSAP MotionPath (preferred — control, alignment, scrub):
js
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: truealign- SMIL (no JS):
svg
<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 (modern, declarative): with
offset-path: path("M10,80 C..."); animation: move 3s linear infinite;and@keyframes move { to { offset-distance: 100%; } }.offset-rotate: auto
- GSAP MotionPath(首选——可控、对齐、可 scrub):
js
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: truealign- SMIL(无需JS):
svg
<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
Animated gradients and filters
动画渐变和滤镜
Gradients: animate or stop offsets. A sheen sweep:
gradientTransformsvg
<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>Filters: animate for gooey/wobble, for focus pulls, or / for glow. Filters are paint-heavy — animate sparingly and prefer / where possible.
feDisplacementMapscalefeGaussianBlurstdDeviationfeColorMatrixfeFloodtransformopacity渐变:动画或停止偏移量。示例为光泽扫过效果:
gradientTransformsvg
<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>滤镜:动画的实现粘性/抖动效果,动画的实现焦点切换,或动画/实现发光效果。滤镜绘制成本高——应尽量少用动画,优先使用/。
feDisplacementMapscalefeGaussianBlurstdDeviationfeColorMatrixfeFloodtransformopacityImplementation choice (pick fast)
实现方式选择(选最快的)
| Need | Use |
|---|---|
| Single declarative draw/fade | CSS |
| Self-contained, no JS bundle | SMIL ( |
| Coordinated, scrubbable, scroll-tied | GSAP |
| Mismatched-point morph | GSAP MorphSVG or Flubber |
| Path following with rotation | GSAP MotionPath / CSS offset-path |
SMIL caveat: not supported in IE/old Edge and historically deprecation-flagged; for max reach or scroll-syncing, prefer CSS or JS. SMIL is still fine for self-contained icon assets in evergreen browsers.
| 需求 | 使用方式 |
|---|---|
| 单一声明式绘制/淡入 | CSS |
| 自包含,无需JS包 | SMIL(SVG内部的 |
| 协同动画、可 scrub、与滚动绑定 | GSAP |
| 点数不匹配的路径变形 | GSAP MorphSVG 或 Flubber |
| 带旋转的路径跟随 | GSAP MotionPath / CSS offset-path |
SMIL注意事项:不支持IE/旧版Edge,且曾被标记为废弃;如需最大兼容性或滚动同步,优先选择CSS或JS。在现代浏览器中,SMIL仍适用于自包含的图标资源。
Authoring and optimization
创作与优化
- Build/clean with SVGO: keep , drop editor metadata, but disable
viewBox/cleanupIdsand any plugin that renames IDs you reference from CSS/JS/SMIL. DisableremoveViewBoxandmergePathsif you animate individual sub-paths or shapes.convertShapeToPath - Inline the SVG in the DOM (not ) so CSS/JS can reach its internals;
<img src>-embedded SVG can only self-animate via internal SMIL/CSS.<img> - Set explicit and avoid fixed
viewBox/widthso the asset scales fluidly.height - For draw-on, ensure paths are actual strokes (), not filled outlines — dashoffset only affects strokes.
fill:none; stroke:... - Respect : gate looping/large motion; keep a static final state.
prefers-reduced-motion
- 使用SVGO构建/清理SVG:保留,删除编辑器元数据,但禁用
viewBox/cleanupIds以及任何会重命名你在CSS/JS/SMIL中引用的ID的插件。如果你要为单个子路径或形状添加动画,请禁用removeViewBox和mergePaths。convertShapeToPath - 将SVG内联到DOM中(而非),以便CSS/JS可以访问其内部元素;
<img src>嵌入的SVG只能通过内部SMIL/CSS实现自动画。<img> - 设置明确的,避免固定的
viewBox/width,以便资源可以流畅缩放。height - 实现绘制效果时,确保路径是实际的描边(),而非填充轮廓——dashoffset仅影响描边。
fill:none; stroke:... - 尊重:限制循环/大动效;保留静态最终状态。
prefers-reduced-motion
Deliver & verify (standalone HTML)
交付与验证(独立HTML)
Packaged helper ():scripts/freezes thescripts/seek-shot.sh anim.html 0 1.5 3harness and screenshots each moment;?t=Ntiles them for one-glance review. Seescripts/contact-sheet.sh sheet.png frame-*.png.scripts/README.md
For a self-contained icon/logo/draw-on the deliverable is one HTML file that opens directly in a browser — inline the SVG in the markup, drive the animation with one mechanism, no build step. One file is the right tier for a vector asset; don't reach for a bundler.
Output contract:
- One file: inline
.html, plus CSS<svg>/ a@keyframeswith GSAP from CDN / SMIL<script>— pick one driver.<animate*> - Include the seek harness matching that driver so any moment can be frozen for a screenshot.
Seek harness — freeze an exact moment. seeks and pauses so a screenshot lands on a still frame. Use the mechanism that matches how the SVG animates:
?t=Nhtml
<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>Verify loop — render → freeze → screenshot → check: open the file at start / mid / end (, , ), screenshot each, and check fidelity (stroke draws in the right direction, morph endpoints clean) plus artifacts (path clipped by , stroke vanishing from a stale dashoffset, FOUC, jank at the morph seam). Any headless tool works:
?t=0?t=<dur/2>?t=<dur>viewBoxbash
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/icon.html?t=0.7" frame-mid.pngBefore you finish:
- Opens standalone — no console errors, inline SVG reachable, CDN (if any) loads.
- The seek mechanism for your driver freezes a deterministic frame.
- Screenshotted at start / mid / end — matches the brief, no clipping or off-strokes.
viewBox - honored — looping/large motion gated, static final state kept.
prefers-reduced-motion - Easing is intentional — /GSAP ease chosen on purpose, no accidental
easedraw-on.linear
打包工具(目录):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,加上CSS<svg>/ 引用CDN的GSAP@keyframes/ SMIL<script>标签——选择一种驱动方式。<animate*> - 包含与驱动方式匹配的seek机制,以便可以冻结任何时刻进行截图。
Seek机制——冻结精确时刻。会跳转到指定时刻并暂停,以便截图获取静态帧。使用与SVG动画方式匹配的机制:
?t=Nhtml
<script>
const t = new URLSearchParams(location.search).get("t");
if (t !== null) {
const N = parseFloat(t);
// SMIL:暂停SVG自身的时钟并调整到指定时刻
const svg = document.querySelector("svg");
svg.pauseAnimations(); svg.setCurrentTime(N);
// CSS @keyframes绘制效果:el.style.animationDelay=(-N)+"s"; el.style.animationPlayState="paused";
// GSAP时间线:tl.pause(); tl.seek(N);
}
window.__ready = true;
</script>验证循环——渲染→冻结→截图→检查:在开始/中间/结束时刻打开文件(、、),分别截图,检查保真度(描边绘制方向正确,变形端点清晰)以及瑕疵(路径被裁剪,描边因过期的dashoffset消失,FOUC,变形接缝处卡顿)。任何无头工具都可使用:
?t=0?t=<dur/2>?t=<dur>viewBoxbash
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/icon.html?t=0.7" frame-mid.png完成前检查:
- 可独立打开——无控制台错误,内联SVG可访问,CDN(如有)加载正常。
- 对应驱动方式的seek机制可冻结确定的帧。
- 在开始/中间/结束时刻截图——符合需求,无裁剪或超出的描边。
viewBox - 遵循——限制循环/大动效,保留静态最终状态。
prefers-reduced-motion - 缓动效果符合预期——/GSAP缓动是有意选择的,而非意外使用
ease绘制效果。linear
Reference files
参考文件
- — full dashoffset math and
references/svg-techniques.mdgotchas, thegetTotalLengthnormalization, GSAP MorphSVG vs Flubber decision guide with code, an icon-toggle morph (hamburger↔close), MotionPath/offset-path details, SMIL-vs-CSS-vs-JS tradeoffs, and an SVGO config tuned for animation.pathLength="1"
- ——完整的dashoffset数学计算和
references/svg-techniques.md注意事项,getTotalLength归一化方法,GSAP MorphSVG与Flubber的选择指南及代码示例,图标切换变形(汉堡↔关闭),MotionPath/offset-path细节,SMIL vs CSS vs JS的权衡,以及针对动画优化的SVGO配置。pathLength="1"