Communitygithub.com

default-explainer

为任何软件产品制作一段45-90秒的精美产品演示视频,从代码渲染为MP4并配以音乐。轻盈明亮画布,简短居中文字,镜头推进产品展示。

default-explainer 是什么?

该技能用于生成高质量的产品演示视频,其核心设计理念是简洁、克制与纯净。它通过一个独立的HTML页面实现,页面根据时间值t渲染任何帧,然后将其渲染为带运动模糊和配乐的MP4视频。视频风格模仿资金充裕的初创公司官网的演示视频:明亮的画布上每次只显示一行简短文字,并聚焦于产品界面的实际交互过程。镜头语言是叙述故事的关键:推进文字、跟随输入、拉回展示结果,然后平滑过渡到下一个功能。 技能工作流程分为三步:首先通过一次对话收集产品名称、核心卖点、品牌信息、格式、时长等必要信息。其次,根据收集到的信息编写脚本和节拍表,并使用提供的HTML模板构建页面。页面必须实现严格的契约,包括纯函数seek(t)、无状态、确定性随机数生成器以及预烘焙历史数据等。最后,渲染成MP4视频并交付。该技能倡导使用真实用户界面、符合品牌规范的字体和颜色、以及精心设计的音效和背景音乐,以营造高级、流畅的观看体验。

兼容平台✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Barty-Bart/motion-graphics/tree/main/skills/explainer-default

在你喜欢的 AI 中提问

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

文档

默认解说视频

一款沉稳、优质的产品演示,就像资金充足的初创公司在其主页上展示的那样。它的全部技巧在于克制:明亮通透的画布,一次只显示一行简短的文字,并以 特写 镜头清晰、干净地重建产品界面,展示其使用过程。镜头负责讲述故事:它推近一段文字,跟随打字动作移动,拉回来揭示结果,然后滑向下一个功能。

你将其编写为一个独立的HTML页面,该页面能根据时间值渲染任何帧,然后将其渲染为带运动模糊和配乐的MP4视频。

步骤 1:获取简报(一条消息,然后开始工作)

在 一条 消息中询问所有内容,然后独立工作。只有产品名称是必需的;对于任何留空的信息,请自行决定并说明你的选择。

  1. 产品:名称,以及用几句话描述其功能。提供网站URL更佳。
  2. 观众必须记住的一件事(例如:“输入一个想法,获得一个实时应用”)。
  3. 品牌:颜色、字体、Logo文件,或“使用网站现有风格”。如果没有,则使用下面的默认调色板。
  4. 格式:16:9 (1920×1080) 或 9:16 (1080×1920)。默认为 16:9。
  5. 时长:此风格的默认时长见下方说明。
  6. 音乐:使用他们自己的音轨(文件路径),或让你合成一个。默认为合成。
  7. 方向(可选):他们想要的场景、标语、行动号召、或任何需要避免的内容。

如果对方提供了URL,在编写任何内容之前,请阅读它(网页抓取)以获取产品的真实措辞、功能、颜色和字体名称。UI标签和标语请使用他们的真实文字。

切勿编造事实。 不得出现虚构的客户数量、收入、评级、推荐或引述。数字只有在用户或网站明确说明时才可使用;否则,请使用相对的条形图、进度条和占位框架线。UI内部的内容示例(提示、文件名、项目名称、收据上的价格)是可以的,并且应该是可信的;但对于产品本身的声明(统计数据、用户数量、评级、像“免费”这样的优惠)则不允许。行动号召只能使用用户或网站提供的URL或优惠;如果没有,则以名称和标语结束。

Logo。 仅当用户提供Logo文件、或者这是他们自己的产品且他们要求使用时,才使用Logo。否则,请使用品牌字体以文本形式写出名称。第三方Logo(集成产品)仅在提供时才使用;否则使用带有名称的中性圆角方块。

同时询问(可选):要展示的 3-5个功能或工作流程步骤,以及产品连接到的任何东西(集成产品)。如果他们提供了URL,请从网站获取这些信息。

此风格的默认时长:60秒。范围 45-90。根据时长分配预算(首先删除可选部分):

时长功能可选部分
45 秒2-3个,每个约9秒佐证或集成,不同时包含
60 秒3-4个,每个约10秒佐证/集成中选一个
90 秒4-5个,每个约12秒两者都包含

步骤 2:编写脚本,然后构建

结构

首先编写脚本和节拍表:屏幕上的每行文字、其后的UI时刻及其时间;将这些时间累加并与上述预算进行比较。屏幕上的文字是唯一的“声音”,因此它必须自行讲述故事。

场景和镜头如何配合: 每个场景(一个标题行,一个UI时刻)都是自己的图层,拥有 自己的镜头。在场景内部,镜头推进、跟随和拉回;在场景之间,淡出的图层通过短暂的模糊(0.3-0.4秒)溶解到下一个已经移动的图层中。

  1. 开场(0-5秒)。 纯亮色画布。一个小的词语在中心淡入:Introducing。它溶解进入品牌时刻:柔和的品牌渐变扫入(丝绸般的斜向光带缓慢飘动),中心是小号的Logo或名称。
  2. 问题或需求(约5秒)。 观众能识别的一行短文字(还在手动建立潜在客户名单?),或者直接进入产品的输入界面:一个大的圆角输入框,为空,带有占位文本,下方有建议标签。
  3. 功能章节(3-5个,每个约8-15秒)。 每个包括:
    • 标题行:一个简短的句子,小号(1080p下约28px),居中在亮色画布上。它逐字打出或淡入。然后 镜头推近 到该行,直到它占据约75%的帧宽,并且 一个关键词或短语被高亮显示:一个柔和的品牌色框从左到右扫过其后方(直接致电决策者。)。
    • UI时刻:产品的真实界面,重建为干净的矢量UI(不是截图),裁剪到只显示相关部分并放大显示。镜头足够近,使元素填满大部分帧,并跟随动作移动:文本输入到输入框中(镜头跟随光标;使用 measureText 测量文本以知道其位置),代理的步骤逐一勾选(旋转器 → 勾选,找到 247 个匹配项),结果卡片堆叠,远处的卡片模糊(景深),表格逐行填充,光标点击单元格按钮并显示值,新列添加时每个单元格在值出现前“思考”并带有闪烁效果,弹出窗口打开显示推理和来源。
    • 拉回:动作结束后,镜头缓慢拉出以显示整个屏幕,停顿片刻,让观众了解他们所在的位置,然后滑向下一个。
  4. 佐证(可选,仅限真实数字)。 一个包含数字的句子,数字会向上计数,数字被高亮(基于一个包含3亿+资料的数据库),下方是微妙的点状地球或网格。
  5. 集成(可选)。 为你的现有工作流构建。 居中显示,同时8-12个应用方块(圆角方形,仅在有真实Logo时才使用;如果集成数量少则重复显示)从边缘以不同深度飘入:近处的稍大且略微模糊,远处的小而清晰,所有方块均带有视差漂移效果。此处文本自行放大,而不是使用镜头推进,以便方块保持在帧内。
  6. 结束语。 在品牌渐变背景上:一个句子,包含一个旋转变化的高亮关键词,循环展示使用场景(用Product完成所有 [潜在客户开发 / 研究 / 外联] 工作。),然后,仅当产品确实有的话,展示另一种使用方式(例如,对于具有Claude连接器的产品,显示 或者直接问Claude),然后是高亮其关键短语的标语。
  7. 结束卡片(3秒)。 品牌渐变背景上的Logo或名称,下方小字显示URL或行动号召。

使其显得高级的规则

  • 一次只做一件事。 屏幕上永远不要同时出现超过一行文字和一个UI元素。文字和UI几乎不共享同一帧:文字场景,然后UI场景。
  • 镜头就是编辑器。 没有硬切。每次场景变换都是一次镜头移动(推进、拉出、滑动)或在亮色画布上的柔和模糊溶解。镜头移动是平滑的缓动(ease,时长0.6-1.2秒)或高度阻尼的弹簧(z ≥ 0.95);无弹跳效果。
  • 真实UI,制作干净。 根据你对产品(或其网站)的了解重建其界面:真实的布局、标签、颜色、字体和圆角半径,并简化为场景所需。其内部的内容是可信的示例数据,符合产品特点。使用柔和阴影、1像素边框、充足的留白。
  • 光标: 标准的macOS风格箭头,以柔和的曲线移动,在点击前稍作停留,点击时有轻微的按下动作和在被点击控件上的柔和波纹。悬停状态会改变。
  • 字体: 如果已知,使用品牌字体;否则使用Inter或Geist。标题行使用小号、常规字重(通过镜头放大,而非字体大小)。大号文字使用紧凑的字间距。
  • 默认调色板: 画布 #F7F7F8,文字 #111114,次要 #8A8A94,UI表面白色带 #E7E7EC 边框,品牌强调色 #2E6BFF,高亮框 = 强调色 15-20% 透明度。品牌渐变:白色 → 浅强调色 → 强调色,呈缓慢的斜向丝绸带(在画布上绘制,而非使用CSS模糊)。品牌颜色替换强调色。
  • 节奏: 每个标题行在显示后停留约2秒;UI时刻以真实速度运行(打字速度约每秒25个字符),每个结果后停顿0.5秒以便观众阅读。
  • 禁止: UI后面的繁忙背景,超过一个强调色,弹跳缓动,表情符号,大块阴影,处处毛玻璃效果,虚构的指标或推荐语。

声音

一首振奋人心、柔和的背景音乐,节奏在100-110 BPM(柔和钢琴或拨弦和弦,开场后轻打击乐进入,结束语前提升情绪),安静而温暖。音效要小:打字时的按键声,柔和的点击声,每个步骤完成时的轻“嘀”声,大幅镜头移动时的轻柔“嗖”声,结束卡片上的温和“叮”声。音乐应像一部优秀产品视频中的背景一样,而非驱动它。

页面构建方式

一个自包含的HTML文件(内联CSS和JS;仅在用户提供时才包含图片)。使用DOM + CSS变换、SVG或一个全屏画布绘制,选择最适合的方式。

页面契约

  • window.VIDEO = {w, h, duration} 和 window.seek(t),这是一个 时间的纯函数:它为时间 t(秒)设置每个元素。没有CSS过渡或动画,没有 setTimeout,没有从上帧继承的状态。任何帧都必须能独立、按任意顺序正确渲染。在 seek 内部测量布局(例如 offsetLeft、measureText)是可以的;这是确定性的。
  • 随机性(颗粒、粒子、音频噪声)来自种子生成器,绝不使用 Math.random。
  • 任何需要历史记录的内容(例如,滞后于移动物体的镜头、模拟)都 在加载时烘焙一次:以 1/240 秒的步长从 0 到 duration 运行它,存储值,并让 seek 通过插值读取它们。对于循环,从循环开始的相应状态启动模拟(参见风格的接缝规则)。
  • 循环: seek(t) 使用 t % duration;跨越接缝的效果(如着陆时的挤压、声音尾音)在两端绘制或混合。
  • 当URL包含 ?render 时:无控件,无预览缩放;舞台精确地位于左上角,大小为 w × h。不包含时:舞台使用CSS缩放以适应窗口,并显示预览控件。
  • 字体:嵌入它们。 下载 woff2 文件(Google Fonts 或品牌字体)并将其内联为 base64 @font-face,以便渲染永不静默回退。对于字重变化,使用可变字体并指定范围([email protected])。
  • 图层: 给场景图层赋予透明背景,并在其后使用一个舞台背景提供基础颜色,这样后面的图层就不会意外遮盖前面的图层。绝对定位的文本应设置 white-space: nowrap。
  • 速度: 大型CSS filter: blur() 和大型模糊渐变在截图时非常慢。在加载时将渐变和辉光一次性绘制在画布上(或作为图像)并移动该图像。

辅助函数:粘贴它们并在其基础上构建

// 阻尼弹簧阶跃响应 0 -> 1。w = 速度(8慢,14正常,22敏捷),z = 阻尼(1 = 无过冲)
function spring(t, w = 14, z = 0.82) {
  if (t <= 0) return 0;
  if (z >= 1) return 1 - Math.exp(-w * t) * (1 + w * t);
  const wd = w * Math.sqrt(1 - z * z);
  return 1 - Math.exp(-z * w * t) * (Math.cos(wd * t) + (z * w / wd) * Math.sin(wd * t));
}
// 一个值多次改变目标,每次进行弹簧运动:track(start, [[time, value, w?, z?], ...]) -> f(t)
const track = (v0, keys) => t => { let v = v0, p = v0; for (const [tk, val, w, z] of keys) { v += (val - p) * spring(t - tk, w, z); p = val; } return v; };
// 精确的缓动关键帧(最适合镜头移动):kf([[t0, v0], [t1, v1, easeFn?], ...]) -> f(t)
const kf = keys => t => { if (t <= keys[0][0]) return keys[0][1]; for (let i = 1; i < keys.length; i++) { const [a, va] = keys[i - 1], [b, vb, e = ease] = keys[i]; if (t <= b) return lerp(va, vb, e((t - a) / (b - a))); } return keys[keys.length - 1][1]; };
const clamp = (x, a = 0, b = 1) => Math.min(b, Math.max(a, x));
const ease = x => (x = clamp(x), x < .5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2);   // easeInOutCubic
const lerp = (a, b, k) => a + (b - a) * k;
const seg = (t, a, b) => clamp((t - a) / (b - a));      // t在 [a, b] 区间内的0..1进度
const rng = s => () => (s = (s + 0x6D2B79F5) | 0, s = Math.imul(s ^ (s >>> 15), 1 | s), s ^= s + Math.imul(s ^ (s >>> 7), 61 | s), ((s ^ (s >>> 14)) >>> 0) / 4294967296);
  • 打字:可见字符串为 full.slice(0, n(t)),光标从 t 开始闪烁。计数:格式化为 lerp(a, b, ease(seg(t, …)))。
  • 镜头 是一个包装元素,其 transform: translate() scale() rotate() 由 kf 或 track 驱动。

声音

  • window.buildAudio(ctx) 从时间0开始在给定的 AudioContext 或 OfflineAudioContext 上调度整个音轨(音乐+效果),使用与视觉效果相同的时间线数字。使用振荡器、滤波噪声(来自种子缓冲区)和包络线合成。如果用户提供了音乐文件,使用 fetch + decodeAudioData 加载它,根据其节拍设置节拍网格(测量它),然后仅在顶部添加音效。
  • 预览播放: 首次播放时,将音轨渲染到一个缓冲区中,然后可以从任何定位位置播放。时钟驱动 seek:
let buf, ac, src, startAt = 0, offset = 0, playing = false;
async function play() {
  ac ??= new AudioContext();
  if (!buf) { const off = new OfflineAudioContext(2, Math.ceil(48000 * VIDEO.duration), 48000); await buildAudio(off); buf = await off.startRendering(); }
  src = ac.createBufferSource(); src.buffer = buf; src.connect(ac.destination);
  src.start(0, offset % VIDEO.duration); startAt = ac.currentTime; playing = true;
}
function pause() { if (!playing) return; offset += ac.currentTime - startAt; src.stop(); playing = false; }
function now() { return playing ? (offset + ac.currentTime - startAt) % VIDEO.duration : offset; }
(function loop() { seek(now()); requestAnimationFrame(loop); })();   // 定位器设置offset(暂停,设置,播放)
  • 将节拍表作为脚本顶部的注释:每个时刻一行,包含时间、发生的事件以及对应的声音。

步骤 3:检查、渲染、交付

在工作文件夹中设置一次环境

...[此处内容已截断以进行翻译]

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

相关技能