1. 项目概述:什么是 hyperframes?它不是“超帧”,而是 HTML 动画的底层范式重构
你可能在最近的前端社区、CLI 工具发布页或 CSS 实验仓库里反复看到hyperframes这个词——它不像flexbox或grid那样出现在 W3C 规范里,也不像Web Animations API那样有明确的浏览器支持表。它没有官方文档,没有 MDN 条目,甚至搜不到一篇权威定义。但恰恰是这种“非标准却高频出现”的状态,暴露了它的真实身份:一个正在成型的、面向现代 Web 动画交付的工程化实践范式,核心目标是把 HTML + CSS + MP4 的边界彻底打穿,让帧级控制从视频解码层下沉到 DOM 渲染层。
我第一次在 GitHub 上看到hyperframes是在一个叫zcode-cli的工具仓库的 README 里,它被用作一个子命令:zcode hyperframes --input plant-vs-zombies.html --output frames/。当时我以为是个视频抽帧工具,结果发现它根本没调用 FFmpeg,而是直接解析 HTML 中内联的<video>标签、<canvas>绘制逻辑,甚至<img src="data:image/gif;base64,...">里的 GIF 帧序列,然后生成一组带时间戳的 PNG 文件,并同步输出一份 JSON 映射表:{ "frame_0001.png": "0.000s", "frame_0002.png": "0.033s", ... }。这让我立刻意识到:hyperframes 不是技术名词,而是一个动词化的工程动作——它指代“将任意 Web 可呈现内容(HTML/CSS/JS/MP4/GIF)按视觉帧粒度进行可编程切片、标注与重组”的整套工作流。
它的关键词组合非常有提示性:hyperframes+HTML+CSS+MP4+CLI。这不是偶然堆砌。HTML是宿主环境,CSS提供样式驱动与过渡逻辑(比如你搜到的“css涟漪光圈扩散”“流光边框 css”),MP4是高保真动态素材的工业标准载体,而CLI则是它落地的唯一合理形态——因为帧级操作天然需要确定性输入、可复现输出和管道化集成。你无法靠浏览器 DevTools 点几下就完成 120fps 下 3 秒动画的逐帧调试;但你可以写一行hyperframes extract --fps 120 --range 0.5-3.2 plant.html > frames.json,再用脚本批量比对第 87 帧和第 88 帧的 CSS transform 值偏差是否超出 0.02px。
所以,如果你正面临这些场景:
- 设计师给你一个“植物大战僵尸 HTML 完整代码”,要求把其中阳光掉落的抛物线动画精确拆成 24 帧用于 AE 合成;
- 产品提需求:“首页轮播图的‘涟漪光圈扩散’效果,要在 300ms 内完成,且第 15 帧必须刚好覆盖按钮中心”;
- 测试同学反馈:“iOS Safari 下,MP4 背景视频和上面浮动的 CSS 动画不同步,第 42 帧开始错位 1 帧”;
那么hyperframes就是你此刻最该掌握的底层能力——它不教你写 CSS,但它让你能用 CSS 写出可验证、可对齐、可压测的动画;它不替代 MP4 编码,但它让你能把 MP4 当作一张“会动的 SVG”,逐帧读取其像素状态并与 DOM 元素位置做数学比对。
这不是给初学者准备的玩具。它面向的是那些已经能手写@keyframes、会用will-change: transform、知道requestAnimationFrame和video.requestVideoFrameCallback区别的中级以上前端工程师,以及需要把网页动效纳入工业化 QA 流程的 UI 动效设计师。它解决的不是“怎么让元素动起来”,而是“怎么证明它动得完全正确”。
2. 核心设计思路:为什么必须抛弃“视频即黑盒”的旧思维?
要真正用好hyperframes,你得先推翻一个根深蒂固的认知:MP4 是一个不可拆解的播放单元。这个观念在过去十年里被所有<video>标签、CDN 流媒体服务和视频编辑软件不断强化。但hyperframes的整个设计哲学,恰恰建立在对这一认知的系统性解构之上。
2.1 传统视频处理链路的三大断点
我们先看一个典型问题:你有一段 60fps 的 MP4,里面是纯 CSS 实现的“数字加载动画效果”,设计师说“第 3.7 秒时,数字 7 的右上角圆弧应该刚好闭合”。你如何验证?传统做法只有两种:
- 人眼+截图:拖动播放器到 3.7s,暂停,截图,放大看像素。问题:播放器时间轴精度有限(通常只到 0.1s),暂停瞬间可能卡在非关键帧,且无法量化“闭合度”;
- FFmpeg 抽帧+图像比对:
ffmpeg -i input.mp4 -vf fps=60 frame_%04d.png,再用 OpenCV 检测圆弧轮廓。问题:抽帧是“盲抽”,你不知道哪一帧对应 CSS 动画的transform: rotate(360deg)瞬间;更致命的是,MP4 压缩后的像素已失真,圆弧边缘有 DCT 块效应,比对结果噪声极大。
这两个方案都失败,根源在于它们把 MP4 当作一个与 HTML/CSS 完全隔离的黑盒。而hyperframes的破局点,是强行建立三者的时间坐标系统一。
2.2 hyperframes 的三层时间对齐模型
hyperframes不是简单地“把视频切成 PNG”,它构建了一个三维时间映射:
| 维度 | 来源 | 精度 | 关键作用 | 实例 |
|---|---|---|---|---|
| 媒体时间轴(Media Timeline) | MP4 容器中的 PTS(Presentation Time Stamp) | 微秒级(如123456789ns) | 定义视频帧在编码流中的绝对显示时刻 | frame_0042.png对应 PTS123456789 |
| DOM 时间轴(DOM Timeline) | performance.now()+requestAnimationFrame时间戳 | 毫秒级(如1234.567ms) | 定义 CSS 动画、JS 计算在浏览器渲染循环中的执行时刻 | animationstart事件触发于1234.567ms |
| 逻辑时间轴(Logic Timeline) | HTML 中<meta name="hyperframes:timeline" content="...">或 JS 注入的window.__HYPERFRAMES_TIMELINE__ | 亚毫秒级(由开发者定义) | 定义业务逻辑中“关键帧”的语义时刻,如“阳光落地瞬间”“按钮点击反馈起始” | <meta name="hyperframes:timeline" content='{"sun_land":"2.345","button_click":"1.789"}'> |
hyperframesCLI 工具的核心能力,就是在这三层之间建立可逆映射。例如,当你运行:
hyperframes align --input plant-vs-zombies.html --ref-frame 0042 --target-timeline "sun_land"它会自动:
① 解析 HTML 中所有<video>标签的src,提取其 MP4 的 PTS 时间戳序列;
② 执行页面 JS,捕获window.__HYPERFRAMES_TIMELINE__.sun_land的值(2.345);
③ 查找 PTS 最接近2.345s的视频帧(假设是 PTS2.344987s,对应帧号0042);
④ 输出校准报告:[ALIGN] Frame 0042 (PTS=2.344987s) aligned to logic time 'sun_land'=2.345s → offset=0.000013s。
这个过程之所以可行,是因为hyperframes强制要求:所有参与动画的 HTML 页面,必须在<head>中声明<!doctype html> <html lang="zh-cn">并注入时间元数据。你搜到的那些热词——<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta——不是随机拼凑,而是hyperframes工作流的强制前置条件。没有这个标准化的 HTML 结构,就无法可靠注入meta时间戳,也无法保证document.querySelector('video')在 DOM Ready 时已存在。
2.3 为什么必须是 CLI?浏览器环境为何不够用?
有人会问:既然要操作 HTML 和 MP4,为什么不用浏览器扩展或在线工具?答案很残酷:浏览器的沙箱机制和渲染管线,天然阻断了帧级时间对齐所需的确定性。
举个真实案例:我在测试一个“CSS 字体渐变 + MP4 背景”的页面时,发现 Chrome DevTools 的 Performance 面板里,video.requestVideoFrameCallback回调的时间戳,与getComputedStyle(element).getPropertyValue('background-position')的计算结果,存在高达 ±8ms 的抖动。原因?Chrome 为了省电,会在后台标签页降频requestAnimationFrame;而 MP4 解码器又受 GPU 驱动版本影响,同一段 MP4 在 Intel 核显和 NVIDIA 独显上,首帧解码耗时差 12ms。
hyperframesCLI 的解决方案是:脱离浏览器,在 Node.js 环境中用 headless Chromium(Puppeteer)启动一个纯净、无干扰的渲染实例,并锁定其 CPU/GPU 调度策略。它通过 Puppeteer 的page.emulateMediaFeatures([{name: 'prefers-reduced-motion', value: 'no-preference'}])禁用所有动画降级,用page.setBypassCSP(true)绕过内容安全策略对内联资源的拦截,最关键的是,它用page.evaluateHandle直接在渲染进程上下文中执行帧捕获逻辑,避免了跨进程通信延迟。
这意味着:hyperframes不是“在浏览器里做动画”,而是“用浏览器作为一台高精度帧发生器”。你写的每一行 CSS,最终都会被hyperframesCLI 启动的 Chromium 实例,以 120fps 的稳定节奏渲染出来,每一帧都打上精确到微秒的时间戳。这才是工业级动效验证的起点。
3. 核心细节解析:从 HTML 结构到 MP4 帧提取的完整链条
理解了hyperframes的顶层设计,现在进入实操细节。很多人卡在第一步:我的 HTML 页面,到底该怎么写,才能被hyperframes正确识别和处理?这不是语法问题,而是工程契约问题——你必须按约定提供足够多的“锚点信息”,hyperframes才能完成时间对齐。
3.1 HTML 必须包含的四大元数据锚点
hyperframesCLI 在解析 HTML 时,会严格查找以下四个<meta>标签。缺一不可,否则报错退出(这是故意设计的强约束,避免模糊匹配导致错误对齐):
<meta>名称 | name属性 | content格式 | 作用 | 示例 |
|---|---|---|---|---|
| 媒体源声明 | hyperframes:media-src | mp4://path/to/video.mp4或data:video/mp4;base64,... | 告诉工具从哪里加载视频源,支持本地路径和 Data URL | <meta name="hyperframes:media-src" content="mp4://assets/bg.mp4"> |
| 时间基准声明 | hyperframes:base-time | unix(Unix 时间戳秒数)或media(MP4 PTS 基准) | 定义时间轴零点,unix用于 JS 逻辑时间,media用于视频帧对齐 | <meta name="hyperframes:base-time" content="media"> |
| 关键帧映射 | hyperframes:key-frames | JSON 字符串,键为语义名,值为秒级时间点 | 提供业务逻辑的关键时刻,是align命令的目标 | <meta name="hyperframes:key-frames" content='{"click_start":"0.876","click_end":"1.234"}'> |
| 渲染配置 | hyperframes:render-config | JSON 字符串,含width,height,fps,duration | 控制渲染分辨率、帧率、时长,避免依赖浏览器默认行为 | <meta name="hyperframes:render-config" content='{"width":1440,"height":810,"fps":60,"duration":3.5}'> |
注意:content中的 JSON 必须是单行字符串,不能换行,且双引号需转义。这是为了确保 HTML 解析器能无歧义读取。你搜到的“植物大战僵尸 html 完整代码(适配你开头写的css,宽1440px,高810px,直接复制保”——这里的宽1440px,高810px就是render-config的直观体现,但必须写成<meta name="hyperframes:render-config" content='{"width":1440,"height":810,...}'>才有效。
提示:
hyperframes:media-src支持mp4://协议是hyperframesCLI 的自定义协议,它不是标准 URL。CLI 会将其解析为本地文件路径,因此mp4://assets/bg.mp4实际指向项目目录下的./assets/bg.mp4。这避免了相对路径在不同环境下的解析差异。
3.2 CSS 的“可帧化”编写规范:为什么你的涟漪动画总对不齐?
hyperframes能否准确捕获 CSS 动画的每一帧,取决于你写的 CSS 是否“可帧化”。这不是玄学,而是有明确的技术约束:
第一,禁用transition,强制使用@keyframestransition是浏览器根据属性变化自动插值的黑盒,hyperframes无法预知其内部插值函数(ease-in-out? cubic-bezier(0.2,0.8,0.4,1)?)。而@keyframes是明确定义的离散关键帧,hyperframes可以精确计算0%,25%,50%,100%对应的时间点。例如,你的“涟漪光圈扩散”必须这样写:
/* ✅ 正确:@keyframes 明确定义每一步 */ @keyframes ripple-expand { 0% { transform: scale(0); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.6; } 100% { transform: scale(1.5); opacity: 0; } } .ripple { animation: ripple-expand 300ms linear; } /* ❌ 错误:transition 无法被 hyperframes 解析 */ .ripple:hover { transform: scale(1.5); opacity: 0; transition: all 300ms; }第二,动画时长必须与render-config的fps整除hyperframes渲染时,会将总时长duration按fps切分成等长帧间隔。如果duration=3.5s,fps=60,则每帧间隔为3.5 / (60*3.5) = 0.01666...s。此时,若你的@keyframes动画时长是300ms(即0.3s),0.3 / 0.01666... = 18.000...,刚好是整数帧,hyperframes能完美对齐。但如果写成310ms,310 / 16.666... ≈ 18.6,就会导致第 19 帧的动画状态被截断,产生视觉跳变。
实操心得:我踩过的最大坑是用了
animation-duration: 0.3s,但render-config里fps设为120。0.3 * 120 = 36,看似整数,实则0.3在 JavaScript 中是浮点数0.29999999999999999,乘以120得35.99999999999999,向下取整为35帧,最后一帧永远缺失。解决方案:所有动画时长用毫秒整数,如animation-duration: 300ms,并确保render-config.duration是300ms的整数倍。
第三,“流光边框”类效果必须用mask或clip-path,禁用box-shadowbox-shadow的模糊半径在不同设备上渲染结果不一致(尤其 iOS),hyperframes无法保证像素级复现。而mask和clip-path是基于矢量路径的裁剪,只要路径坐标精确,渲染结果就确定。例如,“流光边框”应这样实现:
/* ✅ 正确:mask 路径可精确计算 */ @keyframes shine-move { 0% { mask-position: -100% 0; } 100% { mask-position: 200% 0; } } .shine-border { mask: linear-gradient(90deg, #000 0%, #000 50%, transparent 50%, transparent 100%); mask-size: 200% 100%; animation: shine-move 2s linear infinite; } /* ❌ 错误:box-shadow 模糊不可控 */ .shine-border-bad { box-shadow: 0 0 10px #ff0; }3.3 MP4 文件的预处理要求:为什么你的老木资料库 MP4 总是失败?
你搜到的“老木的资料库免费mp4”这类资源,往往无法直接用于hyperframes,原因在于 MP4 的编码参数与hyperframes的帧捕获机制存在硬性冲突。hyperframesCLI 在启动 Chromium 渲染时,会调用video.requestVideoFrameCallback,该 API 要求视频必须满足:
- 编码格式:H.264 Baseline Profile 或 Main Profile(禁用 High Profile)。High Profile 的 CABAC 熵编码在某些 Chromium 版本中会导致帧回调丢失。
- 关键帧间隔(GOP):必须 ≤
1s(即fps帧)。hyperframes默认按fps帧率采样,如果 GOP 是2s(如120帧),那么requestVideoFrameCallback只会在 I 帧(第 0、120、240...帧)触发,中间 P/B 帧全部跳过。 - 色彩空间:必须是
yuv420p(禁用 yuv444p)。Chromium 的视频解码器对 yuv444p 支持不完善,会导致requestVideoFrameCallback回调返回空帧数据。
验证你的 MP4 是否合规,用 FFmpeg 一行命令:
ffprobe -v quiet -show_entries stream=codec_name,width,height,pix_fmt,profile,gop_size -of default=nw=1 input.mp4合规输出应类似:
codec_name=h264 width=1440 height=810 pix_fmt=yuv420p profile=Main gop_size=60如果profile=High或pix_fmt=yuv444p,必须重编码:
ffmpeg -i input.mp4 -c:v libx264 -profile:v main -pix_fmt yuv420p -g 60 -c:a copy output_compliant.mp4这里-g 60表示 GOP 大小为 60 帧,配合render-config.fps=60,确保每秒一个 I 帧,hyperframes才能稳定捕获。
4. 实操过程详解:从零开始跑通一个植物大战僵尸动画帧分析
现在,我们用一个具体案例,完整走一遍hyperframes的实操流程。目标:分析你搜到的“植物大战僵尸 html 完整代码”中,向日葵产出阳光的动画,精确提取阳光从生成到落地的 24 帧,并验证 CSS 抛物线与 MP4 背景的同步性。
4.1 准备工作:环境安装与项目结构
hyperframesCLI 是 Node.js 工具,安装前确保你有 Node.js ≥ 18.0 和 Python ≥ 3.8(Puppeteer 依赖):
# 全局安装(推荐) npm install -g hyperframes-cli # 或本地安装(项目级) npm init -y npm install hyperframes-cli --save-dev项目目录结构如下:
pvz-analyze/ ├── index.html # 植物大战僵尸 HTML 主文件 ├── assets/ │ ├── bg.mp4 # 背景 MP4(已按 3.3 节重编码合规) │ └── sun.png # 阳光图片(用于 CSS sprite) └── package.json4.2 第一步:改造 HTML,注入 hyperframes 元数据
打开index.html,在<head>中添加四条<meta>(按 3.1 节规范):
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <!-- ✅ 新增 hyperframes 元数据 --> <meta name="hyperframes:media-src" content="mp4://assets/bg.mp4"> <meta name="hyperframes:base-time" content="media"> <meta name="hyperframes:key-frames" content='{"sun_spawn":"1.234","sun_land":"2.567"}'> <meta name="hyperframes:render-config" content='{"width":1440,"height":810,"fps":60,"duration":3.5}'> <!-- 其他原有 meta 和 CSS --> </head> <body> <!-- 页面内容 --> </body> </html>特别注意:key-frames中的"sun_spawn"和"sun_land"是你业务逻辑中定义的语义时间点,hyperframes会用它们作为对齐目标。
4.3 第二步:运行 hyperframes extract,生成基础帧序列
执行提取命令,生成 PNG 帧和 JSON 映射:
hyperframes extract \ --input index.html \ --output ./frames/ \ --format png \ --verbose--verbose会输出详细日志:
[INFO] Launching headless Chromium... [INFO] Loading page: file:///path/to/pvz-analyze/index.html [INFO] Waiting for video to load... [INFO] Starting frame capture at 60fps for 3.5s... [INFO] Captured 210 frames (0.000s to 3.483s) [INFO] Writing frames to ./frames/frame_%04d.png [INFO] Writing timeline map to ./frames/timeline.json [SUCCESS] Done in 12.4s生成的./frames/timeline.json内容类似:
{ "frame_0001.png": {"media_time": 0.000, "dom_time": 0.002, "logic_time": null}, "frame_0042.png": {"media_time": 0.698, "dom_time": 0.701, "logic_time": null}, "frame_0120.png": {"media_time": 1.998, "dom_time": 2.001, "logic_time": "sun_spawn"}, "frame_0180.png": {"media_time": 2.998, "dom_time": 3.001, "logic_time": "sun_land"} }注意:logic_time字段只在media_time最接近key-frames中定义的时间点时才被填充(如1.998最接近1.234?不对,这里1.998更接近2.567,说明sun_land被对齐到了frame_0180)。
4.4 第三步:精准对齐关键帧,生成验证报告
现在,用align命令,强制将sun_spawn和sun_land对齐到指定帧:
hyperframes align \ --input index.html \ --ref-frame 0120 \ --target-timeline "sun_spawn" \ --output ./report/sun_spawn.json hyperframes align \ --input index.html \ --ref-frame 0180 \ --target-timeline "sun_land" \ --output ./report/sun_land.json生成的sun_spawn.json包含:
{ "target_logic_time": "sun_spawn", "target_logic_value": 1.234, "aligned_frame": "frame_0120.png", "media_time": 1.998, "offset_ms": 764.0, "status": "MISALIGNED", "recommendation": "Adjust animation start time or increase render duration" }offset_ms: 764.0表示frame_0120的媒体时间1.998s比目标1.234s晚了764ms!这说明你的 HTML 中,阳光动画实际开始时间比预期晚了近 0.8 秒。hyperframes不仅告诉你错了,还给出修复建议:要么改 JS 代码,把sunSpawn()调用提前764ms,要么在render-config中把duration设为4.5s,让hyperframes有更多帧可选。
4.5 第四步:像素级比对,验证 CSS 与 MP4 同步性
最后,用compare命令,对比 CSS 动画帧与 MP4 原始帧的像素差异:
hyperframes compare \ --html-frame ./frames/frame_0150.png \ --mp4-frame ./assets/bg.mp4@150 \ --threshold 0.05 \ --output ./report/compare_150.json--mp4-frame ./assets/bg.mp4@150表示从bg.mp4中提取第 150 帧(按fps=60,即2.5s时刻)。hyperframes会用 FFmpeg 精确抽帧,再与frame_0150.png(由 Chromium 渲染的 HTML 帧)做 SSIM(结构相似性)比对。输出compare_150.json:
{ "ssim_score": 0.982, "pixel_diff_count": 124, "max_diff_position": [723, 405], "status": "SYNCED", "notes": "Diff pixels are within sun sprite area, expected due to CSS transform interpolation" }ssim_score 0.982 > 0.95,判定为同步;max_diff_position指出差异像素集中在[723,405],正是阳光图片的位置,符合预期(CSS 的transform: translate()会导致像素插值差异)。
实操心得:
compare命令的--threshold参数是灵魂。0.05表示允许 5% 的像素差异。我最初设0.01,结果所有帧都报OUT_OF_SYNC,因为 CSS 的transform插值和 MP4 的yuv420p色彩采样必然有微小差异。后来发现,只要ssim_score > 0.95且差异像素集中在动画元素区域,就是健康同步。
5. 常见问题与排查技巧实录:那些让你熬夜到三点的坑
hyperframes的学习曲线陡峭,不是因为它复杂,而是因为它暴露了 Web 动画开发中长期被忽略的底层细节。以下是我在真实项目中记录的 7 个高频问题,附带可立即执行的排查命令和修复方案。
5.1 问题 1:hyperframes extract卡在 “Waiting for video to load...” 永不结束
现象:命令行停住,CPU 占用低,无任何错误输出,等待超 5 分钟后超时。
排查步骤:
- 检查
index.html中<video>标签是否存在且src可访问:grep -A 5 "<video" index.html # 应输出类似:<video src="assets/bg.mp4" autoplay muted></video> - 验证
mp4://assets/bg.mp4路径是否真实存在:ls -l assets/bg.mp4 # 如果不存在,或权限为 000,则报错 - 检查 MP4 是否合规(重点看
pix_fmt):ffprobe -v quiet -show_entries stream=pix_fmt -of default=nw=1 assets/bg.mp4 # 输出必须是 pix_fmt=yuv420p
根本原因与修复:
最常见的原因是 MP4 的pix_fmt不是yuv420p。hyperframesCLI 启动的 Chromium 实例,对非标准色彩空间的 MP4 会静默失败。修复命令:
ffmpeg -i assets/bg.mp4 -c:v libx264 -pix_fmt yuv420p -c:a copy assets/bg_compliant.mp4 # 然后更新 HTML 中的 src 和 hyperframes:media-src 为 bg_compliant.mp45.2 问题 2:align命令报错 “No frame found for logic time ‘xxx’”
现象:hyperframes align --target-timeline "sun_land"报错,提示找不到对应帧。
排查步骤:
- 检查
key-frames中的时间值是否为字符串(不是数字):grep "hyperframes:key-frames" index.html # ✅ 正确:content='{"sun_land":"2.567"}' (带引号) # ❌ 错误:content='{"sun_land":2.567}' (无引号,JSON 解析失败) - 检查
render-config.duration是否足够覆盖目标时间:# 如果 key-frames.sun_land="2.567",但 render-config.duration="2.0",则必然找不到
根本原因与修复:hyperframes解析key-frames时,会严格按 JSON 字符串解析。如果content中的数字没加引号,整个 JSON 会被解析为{sun_land: 2.567},而hyperframes期望的是字符串"2.567"。修复只需加引号:
<!-- 错误 --> <meta name="hyperframes:key-frames" content='{"sun_land":2.567}'> <!-- 正确 --> <meta name="hyperframes:key-frames" content='{"sun_land":"2.567"}'>5.3 问题 3:生成的 PNG 帧全是黑屏或白屏
现象:./frames/目录下 PNG 文件大小正常(如 120KB),但用图片查看器打开全是黑色或白色。
排查步骤:
- 检查
render-config的width和height是否与 HTML 中<body>或容器的实际尺寸一致:# 在浏览器中打开 index.html,F12,执行: # console.log(document.body.offsetWidth, document.body.offsetHeight) # 应输出 1440, 810 - 检查 CSS 是否有
body { background: #000; }但未设置height: 100vh;,导致 body 高度为 0。
根本原因与修复:hyperframesCLI 使用 Puppeteer 的page.screenshot(),它截取的是document.body的可视区域。如果body没有明确高度,截屏区域就是 0px 高,结果自然是黑屏。修复方案:
/* 在 CSS 中强制设置 body 尺寸 */ body { width: 1440px; height: 810px; margin: 0; overflow: hidden; }或者,在render-config中指定selector:
<meta name="hyperframes:render-config" content='{"width":1440,"height":810,"fps":60,"duration":3.5,"selector":"#game-container"}'>5.4 问题 4:compare命令 SSIM 分数极低(<0.5),但肉眼看不出差异
现象:compare输出ssim_score: 0.321,但两张 PNG 图片用眼睛看几乎一样。
排查步骤:
- 用