视频叙事 是做什么的?
如何构建长视频的视觉层,让观众在第九分钟仍能保持方向感。适用于任何带有脚本的“说话人头”视频:新闻评论、单一主题深度解析、对比、教程、列表类内容。
该技能所要取代的失败模式是“场景思维”。每几秒出现一个新图形,迫使观众每几秒就重新开始理解,而没有哪个单独的图形是错误的,这就是为什么反馈总是笼统的“令人困惑”或“信息量太大”,而不是具体的缺陷。
如需中立练习节拍表,请阅读仓库根目录下的 docs/STORYTELLING-WORKBOOK.md。可复用的设计系统位于此技能的 reference/design-system.md 中;已完成的开放循环几何位于 reference/patterns/wall-of-slots/。不需要私有源视频。
这不是 hyperframes-video-beats。那个技能是在说话人头之上放置覆盖卡片。而这个技能设计的是视频 所讲述的 东西本身。
1 · 四大原则
一个持续存在的世界
视频存在于一个单一空间中,并且永远不会重置。观众已经看到的所有内容仍然存在于他们离开时的位置,新的素材被添加到其中,而不是替换它。一个元素一旦被绘制,就永远不会被重绘、移动或重新标记。
此技能中的其他一切都依赖于此。如果空间不断变化,摄像机将无处可去;如果屏幕上没有其他东西,聚光灯就毫无意义;在一个重置的世界中,开放循环无法保持开放。
这个世界也必须值得停留,因此要在构建任何东西之前决定空间语法,并在整个运行时间内保持它。在参考构建中:从左到右是走向结果,向上是走向人类并且是屏幕上唯一温暖的东西,向下是进入记录系统。在前两分钟学会这一点的观众,将不费吹灰之力就能理解视频的其余部分。
为了表达意义,故意且仅一次打破语法。参考构建在十分钟内唯一的一次从右到左移动,是在说到“重新开放率”这个词时,一个已解决的工单反转回队列。它无需一句话就落地,因为方向在此前三分钟里已经意味着某种东西。
摄像机就是剪辑
每次切换到新图形都会让观众重新迷失方向。在一个保持不动的空间中移动则不会让观众付出任何代价。推近的镜头读作“更仔细地看你已经理解的那部分东西”,而拉远则是工作得到回报的地方,因为到目前为止构建的所有东西都还在那里。
在三个高度层级上工作,并在相邻层级之间移动:
| 高度 | 展示内容 | 用途 |
|---|---|---|
| 世界 | 整个空间 | 定位、回报、“我们在这里” |
| 区域 | 正在讨论的子图 | 大部分运行时间 |
| 细节 | 一个元素 | 一个数字、状态变化、决策 |
从细节跳到世界会让人迷失方向,除非这是一个有意的回报节拍,在这种情况下,这是最好的选择。
每条规则都需要一次重构才能学会:
- 将取景命名为精确的世界窗口,而非比例因子。 窗口边界必须落在 标签之间。将取景指定为“比例 0.6”会在帧边缘将单词切成两半。
- 为相同目的重复使用相同窗口。 如果定位镜头是一个特定的窗口,始终使用那个精确的窗口。几乎相同的取景会被读作漂移。
- 根据旅行距离变化持续时间,而不是使用一个全局预设。
- 摄像机和全帧擦拭按顺序进行,从不重叠。 同时进行时,它们会将内容斩断。先是摄像机,然后是擦拭。
- 覆盖层在摄像机在其下方移动之前变为不透明。 相反顺序下,部分移动会暴露出来,读作跳切。
- 标签距离帧边缘至少 60 像素。 元素可以出血并读作“世界在继续”;一个被从中间切断的单词则读作错误。通过放置解决,而不是通过遮罩,因为羽化效果无法捕捉以约 1800 像素/秒速度移动的刀刃。
- 两个连续的同主体移动读作故障。 一个先完成的重取景,然后才开始的一个擦拭,是缩放、完全停止和第二次运动——客户称之为“故障和停顿”。将它们合并为 ONE 手势:共享对称缓动(power2.inOut),落在同一帧上,这样面板读作将素材推到一旁。相关:在 可见素材 移动上使用前端加载缓动(power3/4.out)会在前端加载约 90% 的行程并缓慢停止;将它们保留给元素入场,此时缓慢停止读作稳定动画。(在示例简介中花费了一次客户标记的修订。)
注意力即聚光灯
在任何时刻,只有一个元素处于全亮度。其他所有已绘制的元素保持安静但清晰可读。 不是可选的。这是让一个世界可以累积到十几个元素而从不显得拥挤的规则,也是让拉远镜头可读而不是压倒性的原因。
层次结构来自活动与安静元素之间的 比例,而非让所有东西都变暗。参考构建的第一个原型的最亮像素为 101/255,在 960x540 分辨率下无法阅读;解决方法是使用更亮的墨水,而不是更多调暗。
| 状态 | 目标亮度 (Y, 16-235) | 参考中的不透明度 |
|---|---|---|
| 活动的描边+标签 | 200-235 | 1.0 |
| 上下文,已绘制且仍然相关 | 120-150 | 0.56 |
| 已使用,已完成其作用 | 85-105 | 0.33 |
| 任何标签,任何状态 | 永不低于 90 |
实现是一个函数,在每个旁白节拍处被调用,参数为正在讨论的 id(build_s01.py,lit()):
def lit(t, active, warm=(), dur=0.5, floor=FLOOR):
active = list(active) + [g + "-l" for g in active if g + "-l" in order]
for gid in order:
if gid not in opac: # 从未绘制:无需点亮
continue
tgt = 1.0 if gid in active else (WARM if gid in warm else floor)
if gid not in active:
tgt = min(tgt, MUTED.get(gid, 1.0))
...
其中三个细节是承载性的:
- 标签始终继承其元素的状态(通过
-l后缀折叠)。一个点亮的元素下面有一个不可读的标签,正是要修复的缺陷。 - 已完成其作用的道具通过
MUTED上限退回到不透明度 0,而不是 0.3。静默到 0.3 会在屏幕上方为本节剩余时间留下无法解释的纸屑。生成器规则:在稍后开始的任何lit()之前发出mute(),否则当补间完成时静默会被覆盖。 - 不透明度事件必须按时间轴顺序发出,否则跟踪器会看到过时状态并计算出错误的目标。保持一个
seq()守卫,在发出顺序错误时抛出。 - 每个窗口的每个元素有一个不透明度权限。 两个同时活动的、将同一元素的不透明度驱动到不同值的补间,在独立部分中平滑渲染,但在组装后的母版中会以约 10Hz 闪烁,因为获胜的补间每帧交替出现。参考构建在每个对接点都发布了此缺陷。当像
lit()这样的扫描与显式的变亮或静默重叠时,稍后开始的补间是唯一驱动者,并且扫描跳过这些 id(lit(..., skip=(...)))。防范措施:扫描组装后的渲染,检查每个过渡窗口中是否有单帧的亮度来回跳动(参考构建中的qa/v4/flicker/qa-flicker.mjs)。
开放循环是空间化的
永远不要说“我们稍后再回来”。在世界上留下一些视觉上不完整的东西,然后返回它。观众能看到承诺,就是他们能感受到未完成承诺的承诺。
无论形状如何,机制都是相同的:
- 在钩子中植入它,在空间中,在做出承诺的那行上。
- 在每个边界处重新展示它。 任何章节交接都是边界。重新展示耗时约两秒钟,是保持注意力的机制;跳过它,回报就会变得毫无意义。
- 使其可见地推进。 观众必须看到不完整性的缩小。
- 在你打开的那个图像上关闭。
根据脚本选择形状:
| 脚本形状 | 世界 | 什么是不完整的 |
|---|---|---|
| N 个并行项目 | 脊线上的 N 个插槽 | 空槽位(参见 reference/patterns/wall-of-slots/) |
| 单流程,深入 | 完整的管道,已布局 | 断裂或缺失的链接 |
| 层级或级别 | 向上构建的堆栈 | 堆栈顶部是虚影 |
| 比较 | 共享轴上的两列 | 一列为空,或未填写的计分卡 |
| 新闻或公告 | 时间线或景观地图 | 右侧,未来,是空白的 |
| 教程或一起构建 | 已完成的架构,虚化 | 每个尚未构建的节点 |
将项目交给其插槽是一次实时的摄像机移动,约 1.4 秒,绝不是剪切到已填充状态的预制图片。观众看着他们刚刚了解到的东西变成那个图块。如果视频有数字或计数器,在同一个移动中将其滚动,作为里程表,使用相同的开始时间、持续时间和缓动,否则两个数字会在滚动中途合并成一个条带。
计数器需要一个名称,而不仅仅是一个数字。 角落的数字告诉观众他们在序列中的位置,但没有告诉他们当前项目是 什么;九分钟时,“03”不传达任何信息。将数字与 1-2 个词的标签(SUPPORT, VOICE AI)配对,使用相同的“铭牌”样式,作为与数字相同的刚性固定装置,在同一个里程表滚动中交换。客户对参考构建的评论正是要求这个:视觉效果很好,但观众仍然失去了对屏幕上哪个用例的追踪。
2 · 纪律
如果没有词语,就不绘制
每个形状都是一个带标签的元素、带标签元素之间的连接器、正在移动的主体或环境背景。没有其他东西。
这源于一次静音测试审查,该审查的得分为 7/10(理解度),并指出了原因:图表很干净,但装饰不是。一打未标记的形状以与有意义的形状相同的粗细和调色板绘制,因此首次观看的观众无法区分信号和纹理,并花费真正的注意力来解码那些从未打算被解码的东西。这就是“信息量太大”从文字转移到形状。
预算:每节约 20 个屏幕单词。 没有句子。标签是 1-2 个词,位于其元素下方。参考的第一节在 84 秒内有 21 个单词。
推论:反模式视觉从不覆盖推荐。 首先将世界变到近乎黑色,或者将反例放在好的图表不在的地方。
让观众有一个东西可以跟随
通过一个其状态可见地变化的单一移动主体来承载故事:一个带有数据行和分数栏的芯片、一份文档、一个请求。行被填充,栏移动,边框改变颜色。观众必须能够仅通过观看那个物体来跟随整个章节,并且它决不能传送;它沿着已绘制的连接器以恒定速度移动。
一个全局标记集
完整细节在 reference/design-system.md 中。
| 标记 | 值 |
|---|---|
| 描边 | 1.5 / 2.5 / 4 / 7,没有其他 |
| 字体 | 22 / 30 / 42 / 60 / 84 |
| 圆角 | 10 / 22 / 40,始终是 superellipse(2) |
五种颜色,五种固定含义,零装饰。暖色意味着涉及人类,并且其他任何东西都永远不会是暖色。永远不要使用纯白色作为字体;它在深色背景上会产生光晕。
此纪律防止的失败,来自第一次构建的工艺审查:“每个决策都是本地且正确地做出的;没有做出全局决策。” 18 种描边粗细和 9 种字体大小,其中大多数相差 0.2 个单位,单独看不可见,合起来则是致命的。眼睛得到的是噪音而不是层次结构。使用一个标记检查工具来防范,该工具在构建失败时报告。
动效
入场使用 cubic-bezier(0.16,1,0.3,1),退出比入场更快,持续时间根据质量而变化,因此一个 290px 的节点和一个 40px 的标签不会以相同方式减速,并且 back.out 每节正好一次,用在值得的节拍上。
永远不要使用交叉淡化。 全出血层通过在一个已经不透明的背景上进行裁剪来进入和退出。在任何时刻,一个像素要么是图形,要么是素材。
3 · 构建约定
- 每节一个生成器。
scripts/build_<id>.py只写compositions/<id>.html。编辑生成器,永远不要编辑生成的 HTML。节的根文件index-*.html是手动维护的,因此取景更改在那里进行。 - 共享模块保持共享。 任何定义共享几何的东西都由每节导入。如果节由并行 agent 构建,禁止它们接触共享模块;分叉的几何会无声地破坏每个交接。
- 在组装前为 SVG 定义命名空间。 每节定义自己的
gEdge、shadow、edgeFade。在一个组装后的 DOM 中,url(#id)解析为文档顺序中的第一个匹配项,因此一旦较早的组合被卸载,稍后的每个图形都会绘制 无。参考构建中的代价:35 秒的空屏幕,所有门都是绿色的。只有查看组装后的渲染才能发现它。 - 所有时间对齐到单词的开始,基于单词级别的转录本,在锚定单词之前 0.15-0.25 秒 进入。永远不要将一节均匀地划分为节拍。
- 审核间隙。 任何超过约 1.5 秒且没有任何事情发生的片段都是死屏;让生成器打印它们。
- 在剪辑中留出空气。 激进的停顿压缩会在句子边界产生接近零的间隙,客户会听到“结巴”和跳切式剪辑。根据说话者自己的规范进行校准:标记句子边界间隙比其中位数紧约 10 倍的连接点(参考说话者中位数为 470ms;标记的连接点约为 20-40ms),并通过插入 4-8 帧的空气来修复,不要触碰单词。空气是音轨真实房间音的捐赠切片,带有 10ms 淡入淡出,永远不要使用
anullsrc:相对于 -50dBFS 底噪的数字静音是可听见的缺失。视频保持帧;句子之间,说话人头是近乎静止的,4-8 帧的保持是不可见的。仅插入更改会确定性地偏移转录本,因此无需重新转录。插入的边缘必须尊重语音包络:将捐赠音紧贴单词尾部在 1ms 处会导致可听见的点击;在捐赠音的开始和结束处使用 5ms 淡入淡出,并让淡入的起始点位于单词尾部的能量降至中值以下时,这自然导致插入偏移一个或两个额外的帧。
[其余内容将被翻译]