1实现原理 · 为什么它能做到
核心机制是 SVG stroke draw-on:路径把 stroke-dashoffset 从整长收到 0,视觉上等于从起点到终点画出;配 pathLength="1" 归一化后,任意真实长度的路径都由同一个 0→1 进度值驱动。
A path "draws itself" by animating `stroke-dashoffset` from its full length down to 0 — the dash gap shrinks, exposing the stroke from start to end.
「手跟着笔尖」是这个幻觉的全部:每帧取 getPointAtLength(len*k) 得到笔尖坐标,再把手的笔尖偏移 NIB 对齐到该点;dashoffset 与手位必须由同一个进度值 k 驱动,否则两者会互相追不上。
`getPointAtLength(len * k)` is the workhorse: it returns the on-path coordinate at the same progress driving `strokeDashoffset`, so the nib stays glued to the growing stroke.
节奏由旁白驱动而非固定计时器:每段绘制的总时长约等于它所配的 VO 行(基线 ~2.3 词/秒),被旁白念到的标签要在该词说出的同时画完。
Draw-on duration should track the spoken line, not a fixed timer.
绘制顺序按「人真的会怎么画」编排:SVG 里笔画按绘制顺序排列、按文档顺序动画;元素在旁白顺序里一次只出现一个,每笔落地后留 0.4–1.0s 的 hold,且手要离开板面而不是停在空处。
| Hold | pause hand off-board after element completes (0.4–1.0s) |
素材必须先转成单笔画开放路径:填充团块没有 stroke 可动画,处理方式是先画轮廓再让填充跟上来;手写体用描边字体转路径按阅读顺序逐字母画,或用矩形 mask 横扫假装手写。
Draw-on animates strokes, not fills.
板面需要清理机制:擦除/转场给三条路线(clip-path 或白色矩形擦除、反向 draw-on 回卷、淡出后重画),并要求全片统一擦除语法以免板面堆叠杂乱。
Keep one clear grammar (always wipe left-to-right, say) so transitions feel deliberate.
交付 = 单文件 HTML(内联 SVG 路径按绘制顺序、手部资产内联或 data-URI、一个内联 script 主时间轴)+ ?t=N 冻结;验证必须取至少一个「某个笔画的中途」时刻,因为那才是笔尖贴合幻觉成立或被破的点。
Pick at least one `t` in the *middle of a stroke* — that is where the hand-on-tip illusion is proven or broken.
需要成片时把同一套 draw-on 搬进 Remotion:用 useCurrentFrame()/fps 同时驱动 strokeDashoffset 与手 transform,使渲染逐帧确定(无 timer、无 rAF)。
Drive both `strokeDashoffset` and the hand transform from `useCurrentFrame()` so the render is deterministic — no timers, no rAF.
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | npx / Playwright(无头截图) |
| cli | ffmpeg(contact-sheet.sh 拼图) |
| cli | npx -y playwright(仓库根 seek-shot.sh 内部) |
| package | GSAP 及其 DrawSVGPlugin / MotionPathPlugin(reference 以裸包名 import,未指定 CDN) |
| network | 手写字体(Caveat / Patrick Hand / Shadows Into Light 等),来源由使用者决定(@font-face 自托管或字体 CDN) |
| api | 无第三方业务 API;旁白由使用者自备或宿主侧 TTS 产出 |
4风险提醒 风险提醒:橙色 · 评估后使用
- 复核回路引入远程包与浏览器二进制 — 跑一次验证即从 npm 拉 playwright 并下载 Chromium;离线/内网环境会失败。可改用本地已装的 Playwright/浏览器或人工截图完成同一回路。
- GSAP 插件与字体的外部依赖 — GSAP 路线需 DrawSVGPlugin / MotionPathPlugin(3.12 起免费但仍属外部包);手写字体多来自 Google Fonts 或第三方字体源,交付 HTML 的字体/库端点由使用者决定,宜自托管并固定版本。
- 素材信任面 — skill 要求把用户的插画/字体转成路径并内联进交付 HTML;若素材是来源不明、含 script 或外链的 SVG,会被原样带进产物。使用前应对素材做净化。
- NIB 标定需人工迭代 — 笔尖偏移靠「贴上去截图再调」的循环(文档自述),无自动测量;不同手部资产的偏移不同,换手或镜像后必须重测,易被忽略而导致笔尖悬浮。
- 单文件 HTML 规模上限 — 内联 SVG + 内联手部资产 + 内联 driver 在复杂长片时会迅速膨胀;文档也承认这一点并给出 Remotion 出口,但两条路径的维护成本不同。
5第二遍独立确认
- [ok] 外部依赖调用点复核(npx playwright / ffmpeg / GSAP 插件 / 字体) — SKILL.md Verify loop 内确有 `npx playwright screenshot --wait-for-timeout=500 "file://$PWD/scribe.html?t=1.2" frame-mid.png`;仓库根 scripts/seek-shot.sh 与 contact-sheet.sh 分别为 npx playwright 与 ffmpeg;draw-on-recipes 第 4 节确有 `import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";` 与 MotionPathPlugin 版本;字体建议在 whiteboard-pipeline 第 3 节。四条均落地,非推测。
- [ok] 「手跟笔尖」机制自洽性 — SKILL.md 的 drawOn 与 reference 的 drawOnWithHand 都把同一个 k 同时用于 `path.style.strokeDashoffset = len * (1 - k)` 与 `path.getPointAtLength(len * k)`,并有 liftTo 在笔画间抬手、unDraw 反向跟随——机制自洽,无「手与线不同源」的写法。
- [ok] 无凭证 / 无业务 API 反查 — 三份文档与仓库脚本中无 API_KEY/token/secret/.env 读取、无 curl/wget、无业务端点调用;GSAP 以裸包名 import(无内嵌 CDN URL),字体建议未附 URL。判橙依据是远程包依赖,不含凭据读取。
- [ok] VO 语速口径跨 skill 一致性 — 本 skill 写「Estimate from VO at ~2.3 words/sec」,同 repo 的 explainer-video 写「~2.3 words/sec (140 wpm)」「scene_seconds = line_words / pace + 0.4」——口径一致,跨 skill 可互操作。
- [ok] 目录资产面清点 — skills/whiteboard-animation/ 下仅 SKILL.md(154 行)、references/draw-on-recipes.md(210 行)与 references/whiteboard-pipeline.md(95 行)、0 字节 README.md;无 scripts/、无手部资产实物、无模板/数据——「纯指令 + 内联代码片段」结论成立(手部 PNG/SVG 由使用者自备,skill 只给标定方法)。
- [ok] 跨引用可解析性 — SKILL.md 两处引用均可解析:`scripts/seek-shot.sh`、`scripts/contact-sheet.sh` 位于仓库根(单装本 skill 目录会悬空,需装整个 repo);`see explainer-video` 指向的 explainer-video 确实存在于本 repo 的 skills/ 下。
- [ok] 功能声明 vs 实际能力 — frontmatter 宣称覆盖 SVG stroke draw-on、跟随笔尖的手、错峰揭示、擦除/擦转场与旁白导出,正文与两份 reference 逐项兑现(含 GSAP 与 Remotion 两条实现路径);未声称能自动生出手部素材或自动描摹——素材转换只给方法不给工具。
6结论
e46a8cf637ab1932…3e2d411b72