关于 svg-stroke-reveal
svg-stroke-reveal 是用于网页项目的 SVG 描边显现技能,面向需要绘制动画的标志、签名、图表和路线。它提供进入视口播放一次或与滚动进度同步两种模式,并支持减少动态效果设置。
主要功能
- 01SVG 描边逐步显现
- 02进入视口时播放一次
- 03按滚动进度连续绘制
- 04支持减少动态效果设置
- 05使用原生 HTML、CSS 和 JS
适用场景
- 01标志进入视口时逐笔绘出
- 02签名或图标播放一次绘制动画
- 03路线图随页面滚动逐步显现
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill svg-stroke-reveal安装后在 Claude Code 中输入/svg-stroke-reveal确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/demo.htmlsvg-stroke-reveal.csssvg-stroke-reveal.js详细介绍
技能:svg-stroke-reveal
原生 HTML/CSS/JS 组件——SVG 中的 <path> 线条会像被手绘一样逐渐显现,可根据滚动经过视口的百分比绘制,也可在进入视口时绘制一次。不使用外部库(不使用 Anime.js、GSAP)。
内容
用途与背景
- 用途: SVG 中的
<path>线条根据滚动百分比逐渐显现,或在进入视口时绘制一次(stroke-dasharray/dashoffset),使用原生代码,不依赖外部库;默认使用Rheinmir/uiux-asset中未经修改的原始版本。 - 触发条件(何时使用):
- Logo/签名/图标需要在进入视口时呈现“逐渐绘制”的效果(只运行一次)。
- 折线图、旅程图、路线需要随着滚动进度持续“绘制”(与实际滚动位置关联,而非只运行一次)。
- 用户提到“滚动时绘制 SVG”“svg stroke draw”“logo 逐渐绘制”“path animation reveal”“stroke-dashoffset”或 /svg-stroke-reveal。
- 非目标: 不要用于 7 种常见滚动效果(opacity/translate reveal、文字交错、highlight、nav 收缩、回到顶部、scrollspy、视差)——这些属于
skills/scroll-effects。不要用于尚未转换为轮廓的<text>(见 Rules)。
思维模型
[data-svgr] > <path>×N → getTotalLength() → dasharray = dashoffset = length (隐藏) → mode "once"(IO 触发一次 + CSS transition)或 mode "scroll"(IO 开启标记 + 一个 listener 通过 rAF 根据 progress 写入 dashoffset)→ reduce-motion:立即将 dashoffset 设为 0。
输入和输出约定
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| 输入 | 项目实际使用的 SVG(包含带描边的 <path>) | 是 | 必须先将 <text> 转换为轮廓 |
| 输入 | mode once 或 scroll | 是 | Logo/签名 → once;图表/旅程图 → scroll |
| 输入 | 项目的 design.md | 否 | 如果存在,则按其修改颜色 token |
| 输出 | 添加了 [data-svgr] 的 SVG + 项目中的 CSS/JS | 是 | 原样复制原始文件,或改造 assets/ |
| 输出 | playwright-verify 验证证据 | 是 | 滚动模式需提供滚动前/中/后的截图;once 模式需验证只触发一次 |
规则与能力
- RULE-01(必须):
getTotalLength()根据 path 的内部坐标测量,不受 CSS transform 影响——但是,如果在设置 dasharray 后,通过transform: scale(sx, sy)缩放包含 path 的元素,且sx != sy(两个轴缩放比例不一致),那么绘制速度在像素层面会因两个轴而产生失真,因为动画仍按原始长度运行,而显示效果已被非等比拉伸。始终通过viewBox+width/height设置尺寸,并保持比例一致;避免对包含 path 的元素使用非等比 CSS 缩放。 - RULE-02(必须):
<text>无法进行描边绘制——必须先将文字/字体转换为实际的轮廓<path>(Illustrator“Create Outlines”、Figma“Flatten”或 font-to-path 转换工具),然后才能应用[data-svgr]。直接应用于<text>不会产生任何效果,因为<text>没有getTotalLength()。 - RULE-03(必须):once 模式使用 CSS transition(流畅且 CPU 开销低);scroll 模式每帧通过 JS 直接设置 dashoffset(必须如此才能与滚动位置精确同步)——不要颠倒这两种方式。
- RULE-04(必须):每种模式各使用一个独立的
IntersectionObserver(不要把 once/scroll 的逻辑合并到同一个 observer 中,因为两者行为不同:取消观察一次 vs 持续切换标记);页面上所有 scroll 模式的 SVG 共用唯一一个经 rAF 节流的 scroll listener——不要为每个 SVG 分别添加多个scrolllistener。 - RULE-05(必须):使用
playwright-verify验证:滚动经过 scroll 模式的 SVG 时,截取滚动前/中/后的画面,以确认 dashoffset 逐渐减小;并断言无论多次滚入滚出视口,once 模式的 SVG 都只触发一次。 - 能力:读取原始版本(web/git)、asset skill 和
design.md;将 HTML/CSS/JS 写入目标项目;运行无头浏览器进行验证。
失败边界
- 应用于尚未转换为轮廓的
<text>→ 不会产生任何效果:阻止执行,要求先转换为<path>(RULE-02)。 - 包含 path 的元素被 CSS 非等比缩放 → 绘制速度失真:失败,通过
viewBox+width/height调整尺寸(RULE-01)。 - 验证断言失败(dashoffset 未逐渐减小、once 再次触发)→ 失败,修复后重新验证。
操作方式
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/后续 |
|---|---|---|---|---|---|
| W01 | 判断 | SVG + 要求 | 选择 once 或 scroll 模式;检查 SVG 是否已转换为 <path>(没有 <text>) | mode | <text> → 转换前阻塞 |
| W02 | 操作 | 原始 Pen 表格 | 打开运行链接,将整个 .html 文件(以及 _assets/)复制到项目中,保留署名注释 | 项目中的原始文件 | 不允许拉取外部库 → B01 |
| W03 | 操作 | 文件 + 实际 SVG + design.md | 只修改内容/图片/颜色(替换路径 d=) | 已适配的 SVG | — |
| W04 | 确定性 | 通过 HTTP 运行的页面 | 使用 playwright-verify 验证(RULE-05) | 截图 + 断言 | 红色 → 修复,重复 W04 |
分支
| ID | 类型 | 条件 | 行为 | 跳过/失败 | 汇合 |
|---|---|---|---|---|---|
| B01 | 必需条件分支 | 项目不允许拉取外部库(原始版本) | 使用精简版 vanilla assets/(见 Reference — Assets),阅读后再适配 | — | W04 |
| B02 | 必需条件分支 | prefers-reduced-motion: reduce | 初始化时立即设置 strokeDashoffset = 0,移除 observer/listener | — | W04 |
验证与停止
在 W04 中进行确定性断言(scroll 模式:经过前/中/后三张截图时,dashoffset 逐渐减小;once 模式:即使滚出再滚入,也只触发一次)。需要人工检查:流畅度、笔画顺序。断言通过后停止。
示例
- 正向:“logo 进入屏幕时逐渐绘制” → 在 4 个 path 的 logo 上设置
data-svgr="once"→ 进入视口 30% 时按顺序依次绘制(按 DOM 顺序,stagger 160ms),滚出再滚入时不重新绘制。 - **边界/失败:**签名是使用 script 字体的
<text>→ 没有getTotalLength(),不会绘制任何内容 → 停止,要求将其转换为轮廓(Figma“Flatten”)并生成<path>,然后再应用[data-svgr]。
参考 — 原始版本 — 优先使用(复制整个文件,不要重写)
作者的原始完整代码(公开的 CodePen = MIT)位于外部仓库 Rheinmir/uiux-asset — 可实际运行的图库:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
流程:打开运行链接查看正确效果 → 将整个 .html 文件(以及文件引用到的 _assets/ 文件夹)复制到项目中 → 保留首行署名注释 → 只修改内容/图片/颜色。通过 HTTP 在本地运行,不要使用 file://。
如需获取完整离线版本,可运行 git clone --depth 1 https://github.com/Rheinmir/uiux-asset。
| 原始 Pen — 作者 | 链接 |
|---|---|
| Interactive Cicada Genomics Landing Page — alyona-mysiura | 运行 · 代码 |
| Scroll-Driven SVG Map Editorial Gallery — knyttneve | 运行 · 代码 |
| Scroll-Driven Godzilla Walk-and-Destroy Animation — creativeocean | 运行 · 代码 |
此技能中的 assets/ 只是手写的精简版 vanilla 实现 — 仅在项目不允许拉取外部库时使用;默认使用上述原始版本。
参考 — Assets
assets/demo.html + assets/svg-stroke-reveal.css + assets/svg-stroke-reveal.js — 阅读后适配到项目的实际 SVG 中(替换路径 d=;如有 design.md,则按其调整颜色 token),不要机械照搬。
参考 — 工作机制
- 对
[data-svgr]中的每个<path>:JS 调用path.getTotalLength(),并在初始化时将style.strokeDasharray = length和style.strokeDashoffset = length设为内联样式 → 笔画完全隐藏(虚线长度正好等于 path 长度,偏移量将整段虚线推到视野之外)。 data-svgr="once"模式(仅触发一次 — Logo/签名):IntersectionObserver的 threshold 为 0.3,进入视口时 → 将所有子 path 的strokeDashoffset = 0→ CSStransition: stroke-dashoffset播放一次 → 触发后立即unobserve,即使滚动离开后再滚回来也不会重复播放。data-svgr="scroll"模式(持续进度 — 图表/旅程):IntersectionObserver仅用于切换“位于视口内”标记;标记开启时,由经 rAF 限流的滚动监听器(共用一个监听器,不创建多个)计算progress = (innerHeight - rect.top) / (innerHeight + rect.height),并将其限制在 0..1,然后每帧直接写入strokeDashoffset = length * (1 - progress)— 此模式下不要使用 CSS transition(transition 会导致笔画相对于实际滚动位置滞后)。- 对于包含多个子
<path>的复杂 path(多笔画 Logo、签名)— “once” 模式使用transition-delay: calc(var(--svgr-i) * 160ms)实现错开播放,JS 按 DOM 中 path 的顺序设置--svgr-i(先绘制第一个 path)。 prefers-reduced-motion: reduce→ JS 在初始化时立即将所有 path 的strokeDashoffset = 0,并跳过两个 observer/listener — 立即显示完整笔画,不播放动画。