默认解说视频
一款沉稳、优质的产品演示,就像资金充足的初创公司在其主页上展示的那样。它的全部技巧在于克制:明亮通透的画布,一次只显示一行简短的文字,并以 特写 镜头清晰、干净地重建产品界面,展示其使用过程。镜头负责讲述故事:它推近一段文字,跟随打字动作移动,拉回来揭示结果,然后滑向下一个功能。
你将其编写为一个独立的HTML页面,该页面能根据时间值渲染任何帧,然后将其渲染为带运动模糊和配乐的MP4视频。
步骤 1:获取简报(一条消息,然后开始工作)
在 一条 消息中询问所有内容,然后独立工作。只有产品名称是必需的;对于任何留空的信息,请自行决定并说明你的选择。
- 产品:名称,以及用几句话描述其功能。提供网站URL更佳。
- 观众必须记住的一件事(例如:“输入一个想法,获得一个实时应用”)。
- 品牌:颜色、字体、Logo文件,或“使用网站现有风格”。如果没有,则使用下面的默认调色板。
- 格式:16:9 (1920×1080) 或 9:16 (1080×1920)。默认为 16:9。
- 时长:此风格的默认时长见下方说明。
- 音乐:使用他们自己的音轨(文件路径),或让你合成一个。默认为合成。
- 方向(可选):他们想要的场景、标语、行动号召、或任何需要避免的内容。
如果对方提供了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秒)溶解到下一个已经移动的图层中。
- 开场(0-5秒)。 纯亮色画布。一个小的词语在中心淡入:
Introducing。它溶解进入品牌时刻:柔和的品牌渐变扫入(丝绸般的斜向光带缓慢飘动),中心是小号的Logo或名称。 - 问题或需求(约5秒)。 观众能识别的一行短文字(
还在手动建立潜在客户名单?),或者直接进入产品的输入界面:一个大的圆角输入框,为空,带有占位文本,下方有建议标签。 - 功能章节(3-5个,每个约8-15秒)。 每个包括:
- 标题行:一个简短的句子,小号(1080p下约28px),居中在亮色画布上。它逐字打出或淡入。然后 镜头推近 到该行,直到它占据约75%的帧宽,并且 一个关键词或短语被高亮显示:一个柔和的品牌色框从左到右扫过其后方(
直接致电决策者。)。 - UI时刻:产品的真实界面,重建为干净的矢量UI(不是截图),裁剪到只显示相关部分并放大显示。镜头足够近,使元素填满大部分帧,并跟随动作移动:文本输入到输入框中(镜头跟随光标;使用
measureText测量文本以知道其位置),代理的步骤逐一勾选(旋转器 → 勾选,找到 247 个匹配项),结果卡片堆叠,远处的卡片模糊(景深),表格逐行填充,光标点击单元格按钮并显示值,新列添加时每个单元格在值出现前“思考”并带有闪烁效果,弹出窗口打开显示推理和来源。 - 拉回:动作结束后,镜头缓慢拉出以显示整个屏幕,停顿片刻,让观众了解他们所在的位置,然后滑向下一个。
- 标题行:一个简短的句子,小号(1080p下约28px),居中在亮色画布上。它逐字打出或淡入。然后 镜头推近 到该行,直到它占据约75%的帧宽,并且 一个关键词或短语被高亮显示:一个柔和的品牌色框从左到右扫过其后方(
- 佐证(可选,仅限真实数字)。 一个包含数字的句子,数字会向上计数,数字被高亮(
基于一个包含3亿+资料的数据库),下方是微妙的点状地球或网格。 - 集成(可选)。
为你的现有工作流构建。居中显示,同时8-12个应用方块(圆角方形,仅在有真实Logo时才使用;如果集成数量少则重复显示)从边缘以不同深度飘入:近处的稍大且略微模糊,远处的小而清晰,所有方块均带有视差漂移效果。此处文本自行放大,而不是使用镜头推进,以便方块保持在帧内。 - 结束语。 在品牌渐变背景上:一个句子,包含一个旋转变化的高亮关键词,循环展示使用场景(
用Product完成所有 [潜在客户开发 / 研究 / 外联] 工作。),然后,仅当产品确实有的话,展示另一种使用方式(例如,对于具有Claude连接器的产品,显示或者直接问Claude),然后是高亮其关键短语的标语。 - 结束卡片(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:检查、渲染、交付
在工作文件夹中设置一次环境
...[此处内容已截断以进行翻译]