视差着陆页
将一段短视频转化为一个单屏电影感着陆页——用户的滚动操作会逐帧擦拭视频,同时五个戏剧化的文字场景淡入淡出。页面本身从不滚动,鼠标滚轮/触摸/键盘输入在JS中被拦截并转换为虚拟帧进度。
本技能编码了../../examples/parasites/中包含的参考页面(github / lion / hope 着陆页——三个使用此技能构建的工作页面)中已被验证的模式。如果此处有任何歧义,请以该文件夹为最终依据。
本技能在 YUV.AI 技能体系中的位置
parallax-landing-page 位于 YUV.AI 技能体系的中层,与 yuv-design-system、yuv-decks、yuv-viral-video 和 video-to-landing-page 同级。顶层编排器 yuv-pilot 会在需要将5-15秒视频片段转化为单屏滚动-逐帧着陆页时路由至此。
本技能自带视觉语言(Anton + Caveat + Inter,金/琥珀/强调色/奶油色色板)——它不会调用 yuv-design-system。两者共存:yuv-design-system 控制网页/应用/演示文稿表面的品牌色板,而 parallax-landing-page 专有电影感的滚动-逐帧美学。对于 YUV.AI 视差着陆页,你可以选择在底部区域添加凤凰水印和规范 Linktree URL 作为品牌贯穿线——参见 yuv-design-system/assets/ 中的水印文件。不要将 Anton/Caveat 字体栈替换为设计系统的霓虹色板——那会丢失标志性外观。
你要构建的内容
每个着陆页都是一个锁定视口(viewport),包含:
- 一个全屏画布,每次绘制一帧(cover-fit 模式,DPR ≤ 2)
- 5个绝对定位的文字场景(kicker + 大写 Anton 标题 + Caveat 手写行),每个都有自己的滚动进度窗口
- 一个固定导航栏,包含
PARALLAX // showcase标志和指向兄弟页面的链接 - 一个加载器,在允许用户交互前预加载每一帧
- 一个渐晕(vignette)和胶片颗粒叠加层
- 一个底部的滚动提示,用户开始滚动后淡出
- 一个右下角的帧计数器(例如
073 / 145) - 最终场景就是CTA部分——下方没有单独的
.end区域
在 -q:v 2 质量下,每帧约120-220 KB × 帧数。一个6秒、24fps的片段 = 145帧 ≈ 25 MB。一个10秒的片段 = 241帧 ≈ 53 MB。
保存位置
默认: ~/Documents/yuv-projects/landings/<slug>/——始终将独立的着陆页保存至此以便日后查找。仅当用户明确指定其他位置或选择了“模式 B”(集成到现有的 examples/parasites/ 展示中)时覆盖。
mkdir -p ~/Documents/yuv-projects/landings
cd ~/Documents/yuv-projects/landings
# 技能在此处的 <slug>/ 目录下创建文件夹,同时提取帧并构建HTML。
最终路径(模式 A——独立):~/Documents/yuv-projects/landings/<slug>/。
最终路径(模式 B——集成):用户选择的现有展示目录。
每次构建结束时告知用户最终路径。
工作流程
共有四个阶段。不要跳过任何阶段——每个阶段为下一阶段提供输入。
阶段 1 — 发现
与用户确认:
- 视频路径——源 MP4 文件所在位置。
- 别名 (slug)——简短的文件夹/文件名(例如
hope、marcus、desk)。一个单词,小写,无空格。此名称将同时用作帧文件夹名(<slug>/)和 HTML 文件名(<slug>.html)。 - 主题/话题——这个着陆页关于什么?产品发布?个人时刻?野生动物邂逅?你需要这些信息来撰写文案。
- 项目模式——独立(全新文件夹)还是集成到
examples/parasites/(本仓库顶层)的现有展示中。如果用户说“添加到展示集”,他们指的是模式 B。 - 强调色——用于脚本/CTA 高亮:
gold(#f5b042)、amber(#ff8a3d)、accent(粉色 #ff4d6d)、cream(#f5e9d4),或新的颜色(将其添加到:root的 style.css 中,并编写新的.script.<名称>规则)。
如果用户未指定其中某些项,请提出默认值并继续——不要因为急于等待他们回答所有问题而停滞,他们可以在看到结果后再回答。
阶段 2 — 提取帧
运行内置的提取脚本。它会使用 ffprobe 探测视频,并以原生分辨率、-q:v 2(近乎无损的 JPEG)质量提取每一帧,文件命名为 frame-001.jpg 到 frame-NNN.jpg(3位数字,零填充)。
python <skill>/scripts/extract_frames.py <video_path> <output_folder>
示例:
python C:/Users/User/.claude/skills/parallax-landing-page/scripts/extract_frames.py \
C:/Users/User/Documents/parasites/hope/hope.mp4 \
C:/Users/User/Documents/parasites/hope
脚本会将 JSON 元数据块输出到 stdout。捕获它——你需要 frame_count 和 suggested_scroll_budget 用于 HTML。
为什么使用 -q:v 2:ezgif 等工具会激进地重新压缩(约55 KB/帧)。原生 ffmpeg 在 -q:v 2 下,相同尺寸下每帧约120-220 KB——肉眼明显更清晰。不要降低分辨率;绝不放大(会无端增加文件大小而无细节提升)。
为什么使用滚动预算公式(约26 px/帧):保持每帧所需的滚动像素数在不同片段长度下恒定。145帧 → 约4200,241帧 → 约6300,90帧 → 约2500(下限)。脚本会为你计算并限制在 [2500, 8000] 之间。
阶段 3 — 编写页面
阅读 <skill>/assets/landing-page-template.html 中的模板。它是一个完整的工作文件,包含 {{PLACEHOLDER}} 标记和 <!-- TODO: --> 注释。在 <target>/<slug>.html 处写入新文件,替换所有内容。
替换检查清单(每个占位符都必须替换):
| 占位符 | 放入内容 | 示例 |
|---|---|---|
{{PAGE_TITLE}} | HTML <title> | Hope — The Fastest Heart in the Negev |
{{PAGE_DESCRIPTION}} | meta 描述,一句话 | A moment with Hope, the cheetah of Midbarium. |
{{SLUG}} | 文件夹名(无路径,无斜杠) | hope |
{{FRAME_COUNT}} | 整数帧数 | 241 |
{{FRAME_COUNTER_TEXT}} | 001 / NNN 且宽度匹配 | 001 / 241 |
{{SCROLL_BUDGET}} | 提取器 JSON 中的整数 | 6300 |
{{PREVIEW_FRAME_MID}} | 零填充的中间帧 | 121 |
{{PREVIEW_FRAME_END}} | 零填充的最后一帧 | 241 |
{{LOADER_TITLE}} | 一个单词,大写 | HOPE |
{{LOADER_SCRIPT}} | 手写行,以 … 结尾 | summoning the runner… |
{{NAV_LINKS_HTML}} | 完整的 <a> 标签块用于导航 | 见下方 |
{{SCENE_N_*}} | 每场戏的 kicker、标题、手写行 | 见文案指南 |
{{SCROLL_HINT_TEXT}} | 2-4个单词的大写提示 | SCROLL TO RUN |
{{CTA_BUTTONS_HTML}} | 2-4个 `<a class= |