VHS 录制终端幻灯片演示:用 Tape 脚本编排 slides 翻页动画
VHS 录制终端幻灯片演示:用 Tape 脚本编排 slides 翻页动画
VHS 的 examples/slides 示例展示了一个非常典型且巧妙的用法:把终端幻灯片演示工具 slides 的整场演出录制成 GIF。这篇指南以该示例为骨架,逐行拆解其 Tape 脚本中的全局设置、Hide/Show 帧控制、带速度的 Type@1s 输入与 Ctrl+E 组合键编排,并深入 parser/parser.go、command.go、evaluator.go 源码,帮助你掌握"用低帧率 + 定时按键制造幻灯片翻页节奏"这类演示录制技巧,从而为自己的 CLI 工具或演示应用编写同类 Tape 文件。
一、示例概览:一份 30 行的完整演示脚本
examples/slides/README.md 的核心是一份可直接运行的 Tape 文件,它与同目录下的 slides.tape 内容一致:
Output examples/slides/slides.gif
Set FontSize 32
Set Padding 0
Set Framerate 1
Set Width 1600
Set Height 1200
Hide
Type "slides"
Enter
Show
Sleep 1s
Type@1s "n"
Sleep 1s
Ctrl+E
Sleep 1s
Type@1s "nnn"
Sleep 1s
整个脚本只用了 6 类命令(Output、Set、Hide/Show、Type、Enter、Ctrl、Sleep),却完整模拟了一次幻灯片演示:启动应用 → 隐藏启动过程 → 逐页翻页 → 收尾。这也是 VHS 面向"演示与集成测试"定位的缩影(主 README 开篇即写道 "Write terminal GIFs as code for integration testing and demoing your CLI tools")。
二、逐段拆解:全局设置如何决定画面形态
1. 输出与画布尺寸
Output examples/slides/slides.gif
Set FontSize 32
Set Padding 0
Set Framerate 1
Set Width 1600
Set Height 1200
Output指定渲染产物路径。解析器 parser.go 会读取文件扩展名来决定输出类型:.gif走 GIF 通道,.mp4/.webm走视频通道,.png或带尾斜杠的目录则输出 PNG 帧序列,.txt/.ascii则用于生成 golden file 做集成测试(见 command.go 的ExecuteOutput)。Set FontSize 32:字号越大,终端里每个字符占的像素越多。执行时通过浏览器内核执行term.options.fontSize = 32并调用term.fit重新适配画布(见 command.go 的ExecuteSetFontSize)。Set Padding 0:把终端外框的内边距设为 0 像素,让幻灯片内容尽可能铺满画面(对比 examples/settings 中Set Padding 20的留白效果)。需要提醒的是,evaluator.go 会校验画布尺寸必须能容纳 padding、margin 与 window bar,否则直接报错Dimensions must be at least ...。Set Framerate 1:每秒只捕获 1 帧(对应 command.go 的ExecuteSetFramerate,写入Video.Framerate)。这是整个脚本"幻灯片感"的关键:每秒钟画面只刷新一次,配合下方的Sleep 1s,每一次动作之间恰好只有一帧,GIF 看起来就像一页一页切换的幻灯片,而不是流畅滚动的视频。Set Width 1600/Set Height 1200:16:9 的画布比例,适合宽屏演示场景;高分辨率也保证了幻灯片里较大字号(32px)的文字清晰锐利。
注意:
Set指令(除TypingSpeed外)必须写在文件顶部、任何非设置命令之前。若放在后面会被忽略并输出警告(见 evaluator.go)。本示例严格遵循了这一约束。
2. 隐藏启动过程:Hide / Type / Enter / Show
Hide
Type "slides"
Enter
Show
这是 VHS 最常用的"舞台准备"四件套:
Hide暂停帧捕获(内部调用PauseRecording,见 command.go 的ExecuteHide);Type "slides"在隐藏状态下输入启动命令,Enter回车执行;Show恢复录制(ResumeRecording,见 command.go)。
效果:观众看不到"你敲命令启动应用"的过程,画面直接从已经打开的 slides 界面开始。主 README 的 Hide 小节 也给出同样套路:隐藏阶段可用来做构建、清理等任何不想入镜的准备工作。更妙的是 evaluator.go 专门优化了这条路径——如果设置命令之后的第一个命令是 Hide,VHS 会在正式录制帧之前先执行完 Hide 到 Show 之间的所有命令,确保这中间的按键操作连一帧都不会被记录。
3. 节奏控制:Sleep 定拍
Sleep 1s
Sleep 让 VHS 保持录制但停止交互,时长参数既支持带单位的 1s、100ms,也支持裸数字(默认按秒解释),例如 Sleep 0.5 即 500ms(见 parser.go 的 parseTime 与 command.go 的 ExecuteSleep)。本脚本中每个 Sleep 1s 都夹在动作之间,配合 Framerate 1,每一拍恰好产出一帧。
4. 带速度的输入:Type@1s
Type@1s "n"
Type@<time> 是 Type 命令的速度变体:@1s 表示每个字符之间间隔 1 秒输入。解析层在 parser.go 的 parseSpeed 中识别 @ 后的时间,执行层在 command.go 的 ExecuteType 中逐字符输入并在字符间 Sleep 对应时长。
本示例用它输入 n 和 nnn。结合"每秒 1 字符 + 每秒 1 帧 + 前后 Sleep 1s"的编排可以推断:在 slides 这类演示应用里,n 常被映射为前进/播放下一步,Type@1s "n" 恰好让每次按键的节奏与帧率对齐,从而在 GIF 中呈现出"每帧翻一页"的确定性节奏;而 Type@1s "nnn" 连发三键,则是在演示结束阶段连续翻动多页。相比 Type "n" 的瞬间输入,这种慢速输入能更清晰地传达"用户正在一下一下操作"的体感。
5. 组合键:Ctrl+E
Ctrl+E
Ctrl 命令用于按住 Control 再按下后续键,语法为 Ctrl<a href="https://link.gitcode.com/i/d2ebf00e27829b4ef9e41add5b4a6723" target="_blank">+Alt][+Shift]+<char>,也支持组合修饰键如 Ctrl+Alt+Shift+P(见 [parser.go 的 parseCtrl)。执行时由 command.go 的 ExecuteCtrl 构造键盘动作:先按住 ControlLeft,逐个按下参数中的键,最后一个键用 Type 完成敲击再统一释放。此处 Ctrl+E 即"按住 Ctrl 敲 E",在 slides 演示场景中承担一次独立的翻页/跳转操作(具体语义由 slides 应用自身定义)。
三、参数速查:本例涉及的命令与设置
结合主 README 的 命令参考 与源码,本示例用到的语法归纳如下:
| 命令 / 设置 | 语法 | 取值与说明(仓库依据) |
|---|---|---|
Output |
Output <path> |
按扩展名决定格式:.gif / .mp4 / .webm / .png、帧序列目录、.ascii 测试输出(command.go) |
Set FontSize |
Set FontSize <number> |
像素字号,通过 xterm 的 term.options.fontSize 应用(command.go) |
Set Padding |
Set Padding <number> |
终端外框内边距(像素),command.go |
Set Framerate |
Set Framerate <number> |
每秒捕获帧数;设为 1 即每秒 1 帧,适合幻灯片式演示(command.go) |
Set Width / Set Height |
Set Width/Height <number> |
输出画布宽高(像素),command.go |
Hide / Show |
Hide / Show |
暂停/恢复帧捕获,隐藏启动等前置操作(command.go) |
Type / Type@<time> |
Type "str" / Type@500ms "str" |
逐字符模拟输入,@ 覆盖全局 TypingSpeed(command.go) |
Enter |
Enter |
回车键(键类命令支持 Key<a href="https://link.gitcode.com/i/f76e7b64c4f2b0d34170fa10d4e1d31d" target="_blank">@<time>] [count],[command.go) |
Ctrl |
Ctrl+<char> |
按住 Ctrl 的组合键,支持追加 +Alt/+Shift(command.go) |
Sleep |
Sleep <time> |
保持录制并等待,支持 1s、100ms、裸数字(秒)(command.go) |
四、如何运行与验证
安装 VHS(需 ttyd 与 ffmpeg 在 $PATH 中,详见 README.md)后,进入仓库根目录直接执行:
vhs examples/slides/slides.tape
即可在 examples/slides 目录生成 slides.gif。你可以像这个示例一样,把任意终端演示工具(slides、glow、gum 等,参见 examples 下其他 demo)的操作流程脚本化,并总结出三条可复用的编排原则:
- 先设置、后动作:所有
Set和Output放在文件顶部,保证帧捕获从干净状态开始; - 用
Hide/Show包裹启动与清理:让录制的"正片"从应用界面出现那一刻开始; - 让
Framerate、Sleep与Type@<time>三者节奏对齐:例如"1 帧/秒 + 1 秒一拍 + 每拍一个按键",就能稳定地制造出逐帧翻页的演示效果。
五、相关源码与参考文件
- 示例本体:examples/slides/README.md 与 examples/slides/slides.tape
- 语法解析:parser/parser.go(命令与设置的解析入口)、token/token.go(全部关键字与 token 定义)
- 命令执行:command.go(每个命令的执行函数映射
CommandFuncs与设置映射Settings) - 录制流程:evaluator.go(设置校验、Hide 预执行、帧录制与渲染全流程)
- 完整命令参考:README.md;更多设置示例:examples/settings/README.md
