Communitygithub.com

Lob0Garou/opus-visual-motion-engine

严格的动态图形管线:代码渲染视频/UI、确定性 seek(t)、自动化渲染质量保证、MP4 导出——适用于纯文本模型。

opus-visual-motion-engine 是什么?

Opus Visual Motion Engine 是一个严谨的视觉交付品管线,遵循“规划→从模板构建→渲染→质量保证→修复→导出”的流程。该技能专为 AI 代理(如 Claude Code、Cursor)设计,尤其适用于仅能处理文本的模型,通过代码生成视频、动态图形和用户界面。其核心优势在于确定性:通过 `render(t)` 驱动所有状态,保证 `seek(x)` 在任何时间点都能生成完全相同的帧,从而实现精确的时间同步和可重复的渲染。该技能支持多种动态图形堆栈(如 Vanilla JS、GSAP、p5.js、Three.js),并内置了全面的质量保证机制(qa.mjs),可自动检测常见问题,例如禁止的 API 使用、布局冲突、对比度不足和模板规范违规。该技能还强制规定了一个规划阶段(§1),要求先制定书面计划再编写代码,强调层次结构、节奏和独特的标志性时刻,以生成有意为之的设计,而非泛泛的 AI 输出。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add Lob0Garou/opus-visual-motion-engine

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Opus Visual Motion Engine

一个严谨的视觉交付品管线:规划 → 从模板构建 → 渲染 → 质量保证 → 修复 → 导出。质量来自于渲染前后设置的关卡,而非个人品味。请按顺序执行步骤。即使任务“看起来很简单”,也不要跳过任何一步。

以下路径是相对于此技能目录的(你被给予的 skill_resources 基础目录)。称之为 $SKILL。

0. 先阅读(无条件)

在编写任何代码之前,请阅读与交付品匹配的参考资料。它们在其主题上具有权威性:

交付品阅读
视频、动态图形、解说视频、产品视频references/video-generation.md、references/motion-design.md、references/javascript-animation.md
动态字体 / 以字体为主导的作品以上视频组 + references/typography.md
落地页、仪表盘、模型、UIreferences/visual-design.md、references/typography.md
图表、原理图、数据可视化references/visual-design.md §4
取景 / 镜头决策references/cinematography.md
时长和缓动references/animation-primitives.md
需要同步的音乐或人声SKILL.md §10 + references/video-generation.md §6

工作示例位于 examples/*.md。阅读最接近需求的那个。

1. 在代码之前制定计划(写下来)

在创建 HTML 之前,将计划写在你的回复中(或输出旁边的 PLAN.md 中)。仅存在于你脑海中的计划不算数。

UI / 页面

  1. 核心信息:一句话。观众必须在 3 秒内理解什么?
  2. 具名方向:一个名称,4–6 个具名十六进制标记及其指定含义,带有角色的字体,形态处理方式。从主题的行业、材料和行话中衍生。玩具店和金融科技仪表盘从不共享一个方向。
  3. 层次结构:将每个区块标记为 PRIMARY / SECONDARY / TERTIARY。只有 PRIMARY 获得那一个大胆的动效。
  4. 部分推进:每部分一行,说明其任务以及如何传递给下一部分。
  5. 预渲染计算:文本宽度 ≈ chars × 0.55 × font-size(无衬线字体)或 × 0.5(衬线字体);框宽度 = max(title_chars × 8, subtitle_chars × 7) + 24 以 14px 为基准;行内排列 = Σ宽度 + 间距 ≤ 容器。追踪每个连接器与每个框的关系。如果连接器穿过某个框,则规划一条 L 形路径。

视频 / 动态图形

  1. 核心信息:一句话。如果需要两句,那就是两个视频。
  2. 节拍:3–7 个。每个节拍回答“观众现在知道什么?”
  3. 每个节拍的场景卡:开始/结束(秒),构图的组成、镜头、字号、图元(2–4个)、退出。使用 references/motion-design.md §2 中的模板。
  4. 标志性时刻:命名机制被展示(而非标记)的那一帧(例如,从噪声中过滤出信号)。如果故事仅能通过其字幕工作,请重新设计该节拍。
  5. 全局时间线:绝对时间戳。场景的 EXIT 完成,然后下一场景才能占用该空间。将 DURATION 设置为真实的结束时间(默认为 10–20 秒;仅在有旁白时使用 30–45 秒)。

没有场景卡的场景不属于视频。

2. 从模板构建(视频)

将 assets/composition-template.html 复制到交付品旁边,并填充 EDIT 块。不要重写 ENGINE 块。它们已经提供了:

  • 一个 1920×1080 的 #stage(为 1080×1920 或 1080×1080 更改 STAGE_W/H),适配任何窗口;
  • window.DURATION、window.seek(t)、window.render(t)、window.SCENES:捕获/导出合约(QA 从 SCENES 捕获每个场景);
  • at()、ease.*、lerp、mulberry32(种子化 PRNG)以及用于场景可见性加 EXIT 的 layer();
  • 音频辅助函数 audioAt(t, "rms"|"low")、since(t, list)、hit(t, list, dur),这些函数读取 window.AUDIO(§10),并在没有音频时返回 0;
  • 一个预览播放器(Space、←/→、Home/End),在捕获和减少运动模式下自动关闭;
  • 在所需的 3 层(浅色、受 prefers-color-scheme 保护、[data-theme])中的颜色标记。

对于页面,按照 §6 编写一个普通的单文件 HTML。

3. 确定性时间(视频)

  • render(t) 是唯一的状态驱动程序。每个位置、透明度、大小、数字和 dashoffset 都是根据t(以秒为单位)计算得出的。seek(x) 会产生完全相同的帧,无论之前渲染了什么。
  • t 以秒为单位。将 t 限制在 [0, DURATION] 内,切勿限制在 [0, 1]:将秒限制在 0..1 会静默地冻结整个时间线。
  • 禁止: setTimeout、setInterval、CSS 动画/过渡作为时间线、未种子化的 Math.random()、状态中的 Date.now()。requestAnimationFrame 只能推进播放时钟并调用 render(clock)。模板已经这样做了。
  • 随机性:在构建时通过 mulberry32(seed) 生成,切勿在 render 内部生成。
  • SVG 描边动画需要全部三个属性:pathLength="1"、stroke-dasharray="1" 和 stroke-dashoffset 从 1 到 0。没有 dasharray,线条会从第 0 帧开始完全绘制出来。
  • inline-block 单词跨度会吞掉尾随空格。将间距放在 margin-right 中。

4. 运动语法

五种运动,每种有一个任务。如果一个运动不属于这些类别,请删除它。

  • ENTER:每个页面或场景的一个编排好的序列,按阅读顺序,兄弟元素间隔 60–120ms。每个部分都淡入是 AI 的标志。
  • EMPHASIS:一次一个元素:缩放膨胀(≤1.2)、发光或具有含义的颜色变化。
  • TRANSITION:一个状态变化。变化的部分移动;其他所有部分保持静止。0.2–0.6 秒。
  • CAMERA:阶段级的缩放/平移,每个场景最多一次,0.8–1.2 秒,切勿在文字阅读期间进行。
  • EXIT:0.3–0.5 秒,完全完成。没有元素在场景边界处处于中间状态,除非是一个有意的连续性锚点,该锚点保持不变或变形(同一对象改变状态,这是可用的最强过渡)。

过渡承载含义:硬切 = 类别变化,变形 = 同一事物变化,平移 = 遍历。切忌到处使用默认的交叉淡入淡出。持续时间和缓动函数在 references/animation-primitives.md 中。一起移动的相邻元素应设置不同的持续时间或偏移量。

5. 堆栈决策

情况堆栈
页面微动效CSS 在 transform/opacity 上的过渡,0.2–0.6 秒;循环 ≤2 秒,位于 @media (prefers-reduced-motion: no-preference) 内部
视频 / 动态图形 / 动态字体模板 + 普通 render(t)(默认)
>6 个交错轨迹或 SVG 变形编排GSAP UMD 锁定,一个暂停的主时间线,seek(t) → tl.seek(t)
图表内联 SVG(默认)或 Chart.js UMD 且 animation:false、十六进制颜色(Canvas 无法读取 CSS 变量)
生成艺术p5.js 且 randomSeed/noiseSeed
3D,仅当 3D 是信息本身时Three.js UMD 锁定;在 render(t) 中编写镜头脚本

使用能达到结果的最简单堆栈。外部脚本必须是来自 cdnjs.cloudflare.com 或 cdn.jsdelivr.net 的确切锁定版本。其他所有内容都内联。每种字体都需要一个真正的后备堆栈。

6. 技术底线(不可协商)

  1. 单个 .html 文件,CSS 和 JS 内联,小于 16 MB,图像/数据作为 data: URI。
  2. 颜色作为 :root 标记;暗色层位于 @media (prefers-color-scheme: dark) 下,受 :root:not([data-theme="light"]) 保护,并重复于 :root[data-theme="dark"] 下;显式设置 body 背景。
  3. meta viewport 带有 viewport-fit=cover;env(safe-area-inset-*) 内边距;html/body 上使用 height:100%,切勿使用 100vh;宽块在其自己的 overflow-x:auto 内滚动;网格使用 minmax(0,1fr)。
  4. 显示的计算数字需通过 Math.round、toFixed 或 Intl.NumberFormat。计数器应恰好落在最终值上。
  5. 可访问性:对比度 ≥4.5:1(≥24px 文本时为 ≥3:1)、可见焦点、尊重 prefers-reduced-motion(模板显示稳定的结束帧)。
  6. 可读字体:在 1080p 视频帧中,文本不低于 20px(H/54)。在移动页面上,正文不低于 14px。
  7. 浏览器存储绝不应持有时间线状态。如果使用了存储,将其包裹在 try/catch 中。

7. 避免通用 AI 设计

在需求留有余地的地方,绝不要将其花在这些默认项上(qa.mjs 会自动标记其中几项):

标志修正
米色 #F4F1EA 之类 + 衬线展示字体 + 赭色 #D97757 之类强调色取自主题自身材料的调色板
近乎黑色 + 一种酸性强调色,不论主题如何都使用仅当方向在书面中得到证实时才允许
相同的圆角卡片,所有地方使用相同圆角,相同的柔和阴影按层次结构使用圆角标记;卡片仅用于有边界对象
作为装饰的渐变、紫色渐变、玻璃态纯色填充;最多一个渐变,仅用于连续的物理属性
全大写的眉毛标签、中间点元数据字符串、链接上的“→”使用句子大小写;删除重复内容的标签
默认使用 Inter / 系统字体为此主题选择的字体系列,带有后备堆栈
标题中仅强调一个词仅当强调色是一个语义标记,并在作品的其他地方用于相同含义时才允许(例如,珊瑚色 = 噪声)
通用英雄区:标题 + 副标题 + 行动号召 + 截图以主题世界中最具特色的东西开场
所有内容居中,每个部分使用相同的网格节奏每个部分使用不同的对齐系统;变化节奏
所有元素同时进行动画,所有地方都是 300ms持续时间映射 + 交错(§4)

将大胆用在一个地方,然后移除一个装饰元素。

8. 渲染 + 质量保证(交付前强制进行)

运行自动化质量保证。它会在无头模式下渲染文件并写入证据和结论。需要 Node ≥18。在技能的新副本中首次运行需要在 $SKILL/scripts 目录中执行 npm install(如果 scripts/node_modules 存在则跳过)。

node "$SKILL/scripts/qa.mjs" path/to/deliverable.html

在 Windows 的 DSH 中,使用 pwsh 工具和相同的命令运行它。选项:--step 0.25(为快速场景提供更精细的网格)、--out <dir>、--mode page|video。

视频模式(当存在 DURATION + seek 时自动启用)每步捕获一帧并检查:禁止的 API、乱序重访的确定性、冻结的时间线、无信号区间、近乎空的跨度、不稳定的结束帧、文本碰撞、文本覆盖其他图形、裁剪、对比度、最小字号、从不绘制的 dashoffset 线条、减少运动模式下的循环、控制台/页面错误以及模板痕迹。 页面模式渲染桌面 1440、移动端 390 和深色方案,并检查相同的布局规则以及移动端水平溢出。

输出位于 qa-<name>/ 目录下:report.md(结论和发现)、report.json(每帧的油墨覆盖率和变化)、contact-sheet.png(所有帧合并到一张图片上)、moments.png(取自 window.SCENES 的每个场景的进入、中间和结束)、key_000..100.png 和 frames/。

  • 显式传递标志性时刻和任何同步点:--at 5.4,9.1。
  • 有意的分层(有目的地堆叠的动态字体、覆盖在素材上的标题)应放入 data-qa-allow-overlap 元素中。这将仅针对该子树跳过碰撞和文本覆盖图形检查。切勿用它来掩盖意外的碰撞。
  • font 警告意味着首选的字体家族未加载,文本处于后备字体状态。在判断布局之前修复它,因为所有宽度都是错误的。

退出代码 0 = 无 FAIL。FAIL 是阻塞性的。 每个 WARN 要么被修复,要么在你的评论中用一行文字说明理由。

9. 评论循环

  1. 如果你的模型可以读取图像: 打开 contact-sheet.png,然后打开任何看起来不对的 key_*.png。评判像素,而非你的意图。
  2. 如果你的模型是纯文本的: 使用 report.md 加上 report.json 中的每帧表格。coverage < 0.015 意味着该帧读起来是空的。场景内 diff 为 0 意味着没有移动。diff 的峰值标记了一个剪切点。对照这些数字交叉检查每个场景卡的时间戳。
  3. 用具体的观察(而非“✓”)回答每个问题:
    • 构图:在每个关键帧,视线首先落在哪里?那是信息所在吗?
    • 层次结构:PRIMARY 是否明确无误?每一帧是否恰好有一个重要的移动元素?
    • 机制:标志性时刻是否在移除字幕的情况下展示了想法?
    • 连续性:所有交接是否干净,边界处没有处于中间状态的内容,锚点是否保持不变或变形?
    • 时序:是否存在偏移量,没有默认持续时间,每 8 个单词的阅读时间 ≥1.2 秒?
    • 排版 / 对比度 / 密度:尺寸符合计划,每帧 ≤5 个竞争元素,在深色模式下可读?
    • 叙事:观看者能否在观看一次后复述核心信息?
  4. 修复 → 重新运行 qa.mjs → 重新评论。重复直至 QA 通过且评论中没有未解决的项目。
  5. 最后一个问题,诚实地回答:“这看起来是有意设计的,还是仅仅是生成的?” 如果是“生成的”:指出哪些是通用的,修改计划(§1),然后重建。第一次渲染几乎永远不是最终交付品。预计需要 2–3 次生成。

10. 音频同步(当有音乐或人声时)

切勿凭耳朵猜测时间。将音频转换为数据,让 render(t) 读取它:

node "$SKILL/scripts/audio-data.mjs" music.mp3 --inject composition.html   # 将 window.AUDIO 写入 HTML
  • AUDIO.beats(在估计的节拍下的均匀间隔网格)、AUDIO.onsets(重音、音符、音节)以及每帧的 AUDIO.rms / AUDIO.low(0..1;low 是 <150 Hz 的底鼓/低音段)。
  • 使用模板辅助函数驱动运动:使用 hit(t, AUDIO.beats, 0.2) 进行节拍脉冲(EMPHASIS),使用 audioAt(t, "low") 进行跟随底鼓的缩放,使用 since(t, AUDIO.onsets) 进行每次重音的揭示。将场景剪切放在节拍上:从 AUDIO.beats 中选择场景起始点。
  • 同步从定义上来说是帧精确的:脉冲落在其节拍处或之后的第一个帧上(≤1 帧,30 fps 下 ≤33 毫秒)。如果音频比 t=0 晚开始,则传递 --offset <s>。
  • 在任何人监听之前检查电平。削波的样本会退出代码 1。样本峰值高于 −1 dBFS 会发出警告,因为 AAC 编码会将其削波。报告响度在 −18..−12 LUFS 之外(平台会归一化到大约 −14)。
  • --bpm 可覆盖估计的 BPM。传递 --no-inject 以仅写入 JSON。

相关技能