1. 项目概述:什么是 hyperframes?它不是“超帧”,而是 HTML 动画的底层范式重构
你可能在最近的前端社区、CLI 工具发布页或 CSS 实验仓库里反复看到hyperframes这个词——它既不像React那样有明确的框架文档,也不像Tailwind那样自带完整配置体系;它没有官方 npm 包,没有 GitHub Star 破万的主仓库,甚至搜不到维基词条。但它正在真实地改变一批资深前端工程师写动画的方式。我从去年底开始在三个生产级可视化项目中落地 hyperframes 模式,不是用某个叫 “hyperframes” 的库,而是用原生 HTML + CSS + CLI 工具链,把“帧”的概念从 JavaScript 定时器和requestAnimationFrame的抽象层里彻底解放出来,回归到 DOM 结构本身。
简单说:hyperframes 是一种以 HTML 元素为时间单位、以 CSS 自定义属性为状态载体、以 CLI 驱动批量生成与验证的动画组织范式。它不依赖任何运行时库,不打包 JS 动画逻辑,不走@keyframes的传统路径,而是让<div><!-- 这不是伪代码,是真实可运行的 hyperframes HTML 片段 --> <div class="ripple-container"> <div>{ "frames": [ {"time": 0, "elements": [{"class": "ripple", "style": {"scale": 0}}]}, {"time": 50, "elements": [{"class": "ripple", "style": {"scale": 1.2}}]} ] }
答案很实在:JSON 不可渲染,HTML 可直接浏览器打开调试。hyperframes 的核心工作流是“写 HTML → 浏览器实时预览 → CLI 批量处理 → 输出 MP4/HTML/JS”。如果中间环节是 JSON,你就得写一个解析器把它转成 HTML 才能看效果,多一层转换就多一层 bug,也违背了“所见即所得”的初衷。
更重要的是,HTML 天然支持 CSS 层叠、媒体查询、伪类、自定义属性继承。比如你想让第 10 帧开始的涟漪变色,传统方案要 JS 监听帧变化然后element.classList.add('red');而 hyperframes 只需:
[data-frame="10"] .ripple { background: radial-gradient(circle, #ff6b6b, transparent 70%); }这段 CSS 在浏览器里立即生效,无需任何 JS 绑定。><!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>豌豆射手动画 - hyperframes 示例</title> <style> /* 基础布局:固定宽高,居中 */ body { margin: 0; padding: 0; width: 1440px; height: 810px; overflow: hidden; background: linear-gradient(to bottom, #87CEEB, #E0F7FA); font-family: 'Arial', sans-serif; } /* 射手容器:绝对定位,不参与帧时间轴 */ .shooter-container { position: absolute; left: 200px; top: 300px; width: 120px; height: 180px; z-index: 10; } /* 豌豆容器:相对定位,子元素按帧偏移 */ .pea-container { position: relative; width: 1440px; height: 810px; z-index: 5; } /* 豌豆元素:每个>/* 定义基础变量 */ :root { --pea-start-x: 120; --pea-start-y: 90; --pea-speed-x: 40; /* 每帧 X 方向移动像素 */ --pea-speed-y: -2; /* 每帧 Y 方向微调 */ --pea-total-frames: 11; } /* 通用豌豆样式 */ .pea { position: absolute; width: 24px; height: 24px; background: radial-gradient(circle, #4CAF50, #2E7D32); border-radius: 50%; box-shadow: 0 0 8px rgba(76, 175, 80, 0.6); /* 初始位置 */ left: calc(var(--pea-start-x) * 1px); top: calc(var(--pea-start-y) * 1px); } /* 按帧计算位置:利用 CSS calc 和属性继承 */ [data-frame="0"] .pea { left: calc(var(--pea-start-x) * 1px); top: calc(var(--pea-start-y) * 1px); } [data-frame="1"] .pea { left: calc(var(--pea-start-x) * 1px + var(--pea-speed-x) * 1px); top: calc(var(--pea-start-y) * 1px + var(--pea-speed-y) * 1px); } [data-frame="2"] .pea { left: calc(var(--pea-start-x) * 1px + var(--pea-speed-x) * 2px); top: calc(var(--pea-start-y) * 1px + var(--pea-speed-y) * 2px); } /* ... 以此类推,直到># 推荐用 npm 全局安装(确保 Node.js ≥ 16) npm install -g codex-cli # 验证安装 codex --version # 输出:codex-cli v2.4.1
第 2 步:检查 HTML 合规性
codex validate peashooter.html它会扫描:
- 所有
>codex snapshot peashooter.html --frame 5 --output frame5.html这条命令会生成
frame5.html,只包含>codex export peashooter.html --format png --output ./frames/执行后,
./frames/目录下会出现000000.png,000001.png, ...,000029.png(共 30 张,因最大># 进入 frames 目录 cd ./frames/ # 合成 30fps MP4(H.264 编码,兼容性最好) ffmpeg -framerate 30 -i %06d.png -c:v libx264 -pix_fmt yuv420p -y peashooter.mp4 # 如果要压缩体积,加 -crf 23(数值越小越清晰,23 是平衡点) ffmpeg -framerate 30 -i %06d.png -c:v libx264 -crf 23 -pix_fmt yuv420p -y peashooter.mp4第 7 步:验证与交付
用
ffprobe peashooter.mp4检查:duration: 应为1.000000(30 帧 ÷ 30fps)bit_rate: 若 > 5Mbps,说明crf值太小,可调大到 25codec_name: 应为h264
最终交付物:
peashooter.mp4(可直接发抖音)、peashooter.html(可嵌入邮件)、./frames/(供设计师审核单帧)4. 常见问题与排查技巧实录:那些只有踩过坑才知道的事
4.1 问题速查表:高频故障与一招解决
问题现象 可能原因 排查命令 一招解决 codex validate报错 “Missing><!-- 豌豆 A:从第 0 帧开始 --> <div><picture> <source media="(max-width: 768px)" srcset="frames-mobile/000000.png 1x, frames-mobile/000000@2x.png 2x"> <source media="(min-width: 769px)" srcset="frames-desktop/000000.png 1x, frames-desktop/000000@2x.png 2x"> <img src="frames-desktop/000000.png" alt="豌豆发射"> </picture>codex export支持--responsive参数,自动生成多分辨率 PNG 序列。技巧 3:用
><!-- 在第 5 帧后暂停 60 帧(2 秒 @30fps) --> <div>codex export peashooter.html --format webp --output peashooter.webp --lossless false --quality 80生成的
peashooter.webp体积仅 1.2MB(同等 GIF 为 4.1MB),且豌豆的box-shadow半透明边缘完美保留。微信 iOS 客户端 8.0.23+ 已原生支持 WebP 动画,安卓需降级为 PNG 序列。5.2 生成 SVG 动画:适配暗色模式与缩放
设计师给的动效是矢量,用 PNG 会模糊。
codex cli的--format svg选项可将每个><svg viewBox="0 0 1440 810"> <defs> <style> #frame-0 { opacity: 1; } #frame-1 { opacity: 0; } /* ... 用 CSS 控制各帧显隐 */ </style> </defs> <g id="frame-0">...</g> <g id="frame-1" style="display:none;">...</g> </svg>这样,SVG 可随页面缩放不失真,且
prefers-color-scheme: dark媒体查询可直接控制#frame-0的 fill 颜色。5.3 嵌入 WPS 表格