1. 项目概述:什么是 hyperframes?它不是“超帧”,而是 HTML 动画的底层范式重构
你可能在最近的前端社区、CLI 工具发布页或设计系统文档里反复看到hyperframes这个词——它不像 React、Vue 那样有明确的官网和生态,也不像 Web Animations API 那样写在 W3C 标准里。但它正在真实地改变一批资深前端工程师、交互设计师和动画工具链开发者的工作方式。简单说:hyperframes 不是一个库,不是一个框架,甚至不是一个新语法;它是对“HTML 页面如何承载时间维度内容”这一根本命题的一次重新建模。它的核心思想是——把 HTML 文档本身当作一个可逐帧寻址、可非线性跳转、可带元数据标记的“超媒体时间容器”,而不仅仅是静态结构树。
这个词的构词法很说明问题:“hyper-” 指向超文本(hypertext)的延伸能力,“frames” 则直指时间切片(frame-based timing),合起来就是“可被超链接寻址的帧”。它不依赖<video>标签播放 MP4,也不靠 CSS@keyframes做线性循环;而是让每个 DOM 节点(比如一个<section>、一个<article>、甚至一个<div># 1. 初始化项目(生成基础 HTML 框架) hyperframes init --title "植物大战僵尸教学" --size "1440x810" # 2. 批量导入 PNG 帧(自动创建>.btn { position: relative; overflow: hidden; } .btn::after { content: ''; position: absolute; width: 0; height: 0; border-radius: 50%; background: rgba(0,0,0,0.2); animation: ripple 0.6s linear; } @keyframes ripple { to { width: 200px; height: 200px; opacity: 0; } }
这段代码的问题是:ripple动画是独立于 HTML 结构的,你无法告诉浏览器“这个涟漪对应用户点击‘开始游戏’按钮的第 3 帧”。而在 hyperframes 中,你会这样写:
<button># 1. 确保 Node.js >= 18.17(因需 Web Animations API 的新特性) node -v # 应输出 v18.17.x 或更高 # 2. 全局安装 zcode-cli(它依赖的 @zcode/core 已内置帧解析引擎) npm install -g zcode-cli # 3. 安装 html-build-cli(注意:不是 html-webpack-plugin!这是专用工具) npm install -g html-build-cli # 4. 安装 puppeteer-video-export(需 Chromium) npm install -g puppeteer-video-export # 5. 验证安装(检查是否识别 hyperframes 模式) zcode-cli --help | grep "frame" # 应输出:--frame-mode Enable hyperframes mode for time-based rendering提示:为什么不用
codex cli?实测发现codex cli的--compact模式会压缩 HTML 中的><!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=1440, initial-scale=1.0"> <title>植物大战僵尸教学 | hyperframes 示例</title> <!-- 帧元数据声明:告诉工具这是 hyperframes 文档 --> <meta name="hyperframes-version" content="1.0"> <meta name="hyperframes-canvas" content="1440x810"> <!-- 基础 CSS:重置 + 帧容器 --> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Helvetica Neue", sans-serif; overflow-x: hidden; background: #2c3e50; } /* 帧容器:固定宽高,居中显示 */ .hyperframes-canvas { width: 1440px; height: 810px; margin: 20px auto; position: relative; overflow: hidden; background: #ecf0f1; border: 1px solid #bdc3c7; } /* 默认隐藏所有帧,只显示激活帧 */ [data-frame-id] { display: none; } [data-frame-id].active { display: block; } </style> </head> <body> <div class="hyperframes-canvas"> <!-- 帧内容将插入此处 --> </div> <!-- 帧控制脚本(最小化,不依赖框架) --> <script> // 简单的帧激活逻辑:从 URL hash 读取帧 ID function activateFrame(id) { // 隐藏所有帧 document.querySelectorAll('[data-frame-id]').forEach(el => { el.classList.remove('active'); }); // 显示目标帧 const target = document.querySelector(`[data-frame-id="${id}"]`); if (target) target.classList.add('active'); } // 初始化:从 URL hash 或第一个帧开始 const hashFrame = window.location.hash.replace('#frame=', ''); if (hashFrame) { activateFrame(hashFrame); } else { const firstFrame = document.querySelector('[data-frame-id]'); if (firstFrame) activateFrame(firstFrame.dataset.frameId); } // 监听 hash 变化(支持浏览器前进后退) window.addEventListener('hashchange', () => { const newHash = window.location.hash.replace('#frame=', ''); if (newHash) activateFrame(newHash); }); </script> </body> </html>这个 HTML 的关键点在于:
<meta name="hyperframes-canvas" content="1440x810">:向 CLI 工具声明画布尺寸,后续导出 MP4 时会严格按此分辨率渲染;.hyperframes-canvas容器:固定1440px × 810px,这是你提到的“适配你开头写的css,宽1440px,高810px”的精确实现;[data-frame-id] { display: none; }:所有帧默认隐藏,只通过.active类控制显示——这是 hyperframes 的核心机制,比visibility: hidden更彻底(不占布局空间);- 简洁的 JS:仅 20 行,完成帧激活、URL 同步、历史记录,无任何第三方依赖。
注意:不要用
display: none配合transition做淡入淡出!那是反模式。hyperframes 的过渡由 CSS 的opacity或transform状态变化实现,而非显示/隐藏切换。例如,>[data-frame-id="zombie-approach"] .zombie { transform: translateX(1200px); /* 初始位置在右侧外 */ opacity: 1; } [data-frame-id="zombie-approach"].active .zombie { transform: translateX(200px); /* 进入位置 */ transition: transform 2s ease-in-out; }这样,当
.active类被添加时,CSS 自动触发动画,无需 JS 控制。3.3 构建植物大战僵尸的 7 个核心帧:从结构到语义
现在,我们往
.hyperframes-canvas里填充真实的帧内容。根据植物大战僵尸的经典流程,我提炼出 7 个不可省略的核心帧(每个都带><div class="hyperframes-canvas"> <!-- 帧 1:标题页 --> <section># 1. 进入项目目录 cd /path/to/your/project # 2. 用 zcode-cli 解析帧结构(验证语义正确性) zcode-cli parse index.html --frame-mode --strict-semantics # 输出应显示:7 frames parsed, dependencies validated, no circular references # 3. 用 html-build-cli 注入增强功能(自动生成导航栏、帧进度条) html-build-cli build index.html \ --output dist/index.html \ --canvas 1440x810 \ --add-nav \ --add-progress \ --add-keyboard-controls # 4. 导出为 MP4(使用 puppeteer-video-export) puppeteer-video-export \ --url "file:///path/to/your/project/dist/index.html" \ --output "pvz-tutorial.mp4" \ --width 1440 \ --height 810 \ --fps 30 \ --duration 51s \ --wait-for-frame "victory" # 确保渲染到最后一帧这个流程的精妙之处在于:
zcode-cli parse不仅验证帧 ID 是否唯一、依赖是否闭环,还会检查>/* 错误:试图用 CSS 动画控制帧 */ [data-frame-id="zombie-approach"] .zombie { animation: move-zombie 12s linear forwards; } @keyframes move-zombie { from { transform: translateX(1200px); } to { transform: translateX(200px); } } /* 正确:定义“zombie-approach”帧下的终态 */ [data-frame-id="zombie-approach"] .zombie { transform: translateX(200px); /* 终态位置 */ opacity: 1; } /* 如果需要过渡效果,单独定义过渡规则 */ [data-frame-id="zombie-approach"] .zombie, [data-frame-id="title"] .zombie { transition: transform 0.3s ease, opacity 0.3s ease; }这种写法的优势是颠覆性的:
- 可预测性:
getComputedStyle(element).transform总是返回确定值,便于自动化测试;- 可组合性:你可以