📄 文档列表
🎬 口播文案
✏️ 编辑文档
标题
工具栏
加粗
H2 标题
H3 标题
引用
无序列表
有序列表
代码块
📷 上传图片
点击或拖拽上传图片
支持 PNG, JPG, GIF, WebP 格式
内容 (Markdown 格式)
最近在用 pi 跑一个小项目的时候,发现 HyperFrames 这个新框架——它能把浏览器里的 Lottie 动画原样渲染成 1080p 的视频。我当时就有个想法:能不能用 pi 写代码,搭一套 Lottie + HyperFrames 的演示视频,把"为什么 Lottie 适合做技术演示"这件事讲明白? 于是就有了 `C:/Users/Administrator/lottie-video/` 这个项目。视频只有 18 秒,4 个分镜,2.6 MB,0 lint 错误。过程里踩了一堆坑,今天把完整流程和血泪教训都写下来。 ## 一、三件套分别做什么 在动手前先明确分工,省得后面搞混。 **pi** 是编码 Agent。我用 `pi -c "..."` 让它帮我把 4 个分镜的 HTML 写出来,同时在踩坑时直接问它"这个报错是什么",省了大量查文档的时间。 **HyperFrames** 是一个视频生成框架,它的核心思路是:把浏览器里跑的东西(HTML + CSS + Lottie)当作"分镜",用 Puppeteer 逐帧截图,再 ffmpeg 拼成视频。和 Remotion 比,它的优势是**完全兼容 lottie-web**——你写好 Lottie 加载代码,框架会自动捕获每一帧。 **Lottie** 是 Airbnb 开源的 JSON 动画格式,体积小、矢量、可时间寻址。这次我从 LottieFiles 公开 CDN 下了 3 个动画: | 文件 | 用途 | 大小 | |------|------|------| | `assets/checkmark.json` | 庆祝纸屑(confetti)| 614 KB | | `assets/loading.json` | 浮动粒子 | 8 KB | | `assets/rocket.json` | 火箭发射 | 2 KB | ## 二、项目结构 整个项目只有 8 个文件,目录非常干净: ``` lottie-video/ ├── index.html # 主时间线(18 秒) ├── hyperframes.json # 框架配置 ├── meta.json # 项目元信息 ├── package.json # 依赖 hyperframes 0.6.56 ├── assets/ │ ├── checkmark.json # 614 KB confetti │ ├── loading.json # 8 KB │ └── rocket.json # 2 KB ├── compositions/ │ ├── scene1-rocket-launch.html # 0-4s │ ├── scene2-features.html # 4-10s │ ├── scene3-confetti-celebration.html # 10-14s │ └── scene4-outro.html # 14-18s └── lottie-demo.mp4 # 最终输出 2.6 MB ``` 4 个分镜用 `<div class="clip">` 包裹,每个 clip 显式声明 4 个 `data-*` 属性:开始时间、结束时间、HTML 源、音频。`index.html` 里的 GSAP 主时间线只在 seek 到对应时间点时调用 `lottie.goToAndStop()` 来精确控制每个动画的当前帧。 ## 三、4 个分镜的设计逻辑 **Scene 1 火箭发射(0-4 秒)**:开场要冲击力。一颗紫粉渐变的火箭从画面下方飞升,背景是深紫到深蓝的径向渐变。底部中文字幕"用 After Effects 动画,在浏览器里渲染成视频",提前预告核心技术。 **Scene 2 三大特性(4-10 秒)**:3 张并排卡片,每张卡片顶部是"VECTOR/LIGHTWEIGHT/SEEKABLE"双语标签,中间是紫粉渐变的 stat 数字(`∞ / 10× / 100%`),下方是 3 行 feature-item 文字。卡片中央各有一个旋转光环 logo——外环顺时针 4 秒一圈,内环逆时针 3 秒一圈,核心球 1.6 秒呼吸一次。 **Scene 3 庆祝纸屑(10-14 秒)**:confetti 满屏飘落 + 4 角 sparkle + 8 颗彩色粒子向 8 个方向飞出 + 大冲击波 shockwave + 中心 burst Lottie + 旋转光环 + "READY TO SHIP" 徽章 + "立即发布" 4 字居中。这一帧信息密度最高,所以文字背板用 4 层 text-shadow 强化可读性。 **Scene 4 终端打字机(14-18 秒)**:黑色终端外壳,三行代码用 GSAP 拆字符逐字打出——`npx --yes hyperframes@0.6.56` 第 1 行,`render --output lottie-demo.mp4` 第 2 行,`--quality draft --fps 30` 第 3 行。每打出一行后,紫色光标闪 2 次(`yoyo: true, repeat: 1`),然后下一行开始。 ## 四、Lottie 集成规范 这一步是整个项目最关键的技术点,写错了就白干。 **核心模式**: ```js const anim = lottie.loadAnimation({ container: document.getElementById("xxx"), renderer: "svg", loop: false, // 或 true(lint 允许) autoplay: false, // 关键:必须 false,由 GSAP 控制 path: "../assets/xxx.json", }); window.__hfLottie = window.__hfLottie || []; window.__hfLottie.push(anim); ``` **3 个必填规范**: 1. **`autoplay: false`** — 必须关掉自动播放,由 GSAP 主时间线统一控制。如果开 autoplay,每个 Lottie 会在加载时自己跑,到渲染时已经播完了 2. **`loop: false`**(一般情况)— 一次性动画设为 false,循环动画(如 Scene 3 中心 burst)可以设 true。GSAP 会在 seek 到对应时间点时调用 `goToAndStop(frame, true)` 跳转到指定帧 3. **注册到 `window.__hfLottie`** — HyperFrames 在渲染时需要枚举所有 Lottie 实例来 seek。漏注册 = 渲染时 Lottie 卡在第 0 帧 ## 五、GSAP 时间线编排 主时间线挂在 `window.__timelines`,**必须 `paused: true`**: ```js const tl = gsap.timeline({ paused: true }); window.__timelines = window.__timelines || []; window.__timelines.push(tl); ``` **Scene 4 打字机实现**(每个字符包 `<span class="ch">`,空格替换为 ` `): ```js const text = "npx --yes hyperframes@0.6.56"; const container = document.querySelector(".typed-text"); container.innerHTML = text.split("").map(c => c === " " ? '<span class="ch"> </span>' : `<span class="ch">${c}</span>` ).join(""); tl.to(".typed-text .ch", { opacity: 1, duration: 0.04, stagger: 0.05 // 每个字符间隔 0.05 秒 }, 14.0); // 14 秒开始打字 ``` 打字完成后,光标闪 2 次(0.4 秒一闪): ```js tl.to(".cursor", { opacity: 0, duration: 0.4, yoyo: true, repeat: 1 }, 16.5); ``` ## 六、踩坑记录(重要!) 这部分是这篇最有价值的部分,建议认真看。 **坑 1:GSAP `rotation` 对 border 圆环不触发** 我把卡片中央的 logo 设计成"双环 + 核心球",外环用 border 描边模拟旋转圆弧。但 GSAP 的 `tl.to(".ring", { rotation: 360 })` **完全不生效**——双环静止不动。 排查后发现:GSAP 的 `rotation` 走 transform matrix 矩阵,但 `border-top-color + border-right-color + border-radius: 50%` 这种"半圆弧"在某些渲染路径下 transform 不触发合成层。 **解决**:放弃 GSAP,改用 CSS `@keyframes`: ```css @keyframes spin-cw { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .middle-logo-ring { animation: spin-cw 4s linear infinite; transform-origin: 50% 50%; /* 显式声明,Skia 渲染更稳 */ } ``` **坑 2:GSAP `repeat: -1` 触发 lint 错误** HyperFrames 的 lint 规则会拒绝 `repeat: -1`(无限循环),报错 `gsap_infinite_repeat`。 **解决**:用有限计数公式: ```js // 视频总时长 18 秒,循环周期 4 秒 → repeat 3 次 const duration = 18; const cycleDuration = 4; tl.to(".ring", { rotation: 360, duration: cycleDuration, repeat: Math.floor(duration / cycleDuration) - 1 }); // 注意:yoyo 时 cycleDuration 翻倍 ``` **坑 3:`backdrop-filter: blur(28px)` 让文字模糊** Scene 3 我想给"立即发布"做一个磨砂玻璃背板,于是加了 `backdrop-filter: blur(28px)`。结果文字在模糊层上面显得发虚,渲染出来像近视眼没戴眼镜。 **解决**:去掉 backdrop-filter,改用纯色渐变背板 + 多层 text-shadow 模拟光晕: ```css .center-content { background: linear-gradient(135deg, rgba(10, 10, 30, 0.92) 0%, rgba(20, 20, 50, 0.88) 50%, rgba(10, 10, 30, 0.85) 100%); /* 不要 backdrop-filter */ } .big-text { text-shadow: 0 0 24px rgba(139, 92, 246, 1), 0 0 48px rgba(139, 92, 246, 0.8), 0 0 80px rgba(236, 72, 153, 0.7), 0 4px 16px rgba(0, 0, 0, 0.9); } ``` **坑 4:`background-clip: text` 在 Skia 渲染不稳定** Scene 3 想给"立即发布"做紫粉橙三色渐变文字,用了 `background-clip: text`。在 Chrome 里看着正常,但 HyperFrames 用 Puppeteer 渲染出来时文字要么透明要么颜色错乱。 **解决**:放弃 gradient 文字,改用纯白 + 6 层 text-shadow(紫光晕 + 粉光晕 + 橙光晕 + 黑色硬投影)。视觉效果比 gradient 更稳。 **坑 5:元素层级遮挡** Scene 3 信息密度高,confetti 背景、8 颗彩色粒子、旋转光环、burst Lottie 都在 0-3 层之间。我一开始把"立即发布"文字也放在 z-index: 3,结果被粒子和光环挡住。 **解决**:强制新建 stacking context + 提高 z-index: ```css .center-content { z-index: 100; /* 从 3 提到 100 */ isolation: isolate; /* 强制新建 stacking context */ } ``` 背板用 4 层:径向渐变(0.78→0.15 透明度)+ 28px blur + 80px 紫光 + 60px 黑色阴影。 **坑 6:npm cache 锁冲突** `npx --yes hyperframes@0.6.56 render` 报 `EEXIST` 错。 **解决**: ```bash rm -rf C:/Users/Administrator/AppData/Local/npm-cache/_npx/ ``` **坑 7:字体警告 `font_family_without_font_face`** Lint 提示我用了 `"PingFang SC", "Microsoft YaHei"` 但没加 `@font-face`。 **解决**:直接移除这行中文字体声明,让浏览器回退到系统默认字体(宋体/微软雅黑)。HyperFrames 渲染时用的是真实浏览器,中文字体不会丢。 **坑 8:音频末尾 2 秒静默** 我的背景音乐只有 16 秒,视频是 18 秒。最后 2 秒没声音,听起来很突兀。 **解决**:两种方案—— - 方案 A:用 FFmpeg 延长音频(`ffmpeg -i in.mp3 -filter:a "apad=pad_dur=2" out.mp3`) - 方案 B:`<audio>` 标签加 `loop="true"`,但会有 1 秒断点 - 我选了方案 C:把视频缩到 16 秒,或者在最后 2 秒加个静音但有字幕的过渡 最后我把视频时长和音频对齐了。 ## 七、渲染命令 整个项目最关键的一行命令: ```bash npx --yes hyperframes@0.6.56 render \ --output lottie-demo.mp4 \ --quality draft \ --fps 30 ``` `--quality draft` 渲染快(2 分 52 秒),`--quality production` 慢 4 倍但质量更高。发布前我一般先 draft 跑一遍看效果,确认后再 production 一次。 `--fps 30` 是默认值。如果动画有高速运动(比如粒子爆炸),可以加到 60,但文件大小会翻倍。 **完整流程**: ```bash # 1. lint 检查 npx --yes hyperframes@0.6.56 lint # 2. 渲染(先 draft 验证) npx --yes hyperframes@0.6.56 render --output lottie-demo.mp4 --quality draft --fps 30 # 3. 抽帧验证 ffprobe lottie-demo.mp4 ffmpeg -i lottie-demo.mp4 -vf "select='eq(n,90)+eq(n,180)+eq(n,270)'" -vsync 0 frames-%02d.png # 4. 生产级渲染 npx --yes hyperframes@0.6.56 render --output lottie-demo.mp4 --quality production --fps 30 ``` ## 八、性能数据 最终视频: | 指标 | 数据 | |------|------| | 文件大小 | 2.6 MB | | 时长 | 18.02 秒 | | 分辨率 | 1920×1080 | | 帧率 | 30 fps | | 视频编码 | h264 | | 音频 | aac 48 kHz 立体声 | | Lottie 总数 | 29 个动画实例 | | Lint | 0 errors / 0 warnings | | 渲染时间(draft)| 2 分 52 秒 | | 渲染时间(production)| 约 11 分钟 | 29 个 Lottie 同时跑 18 秒,2.6 MB 压缩比非常高——比用 After Effects 导出再转码的 MP4 小一个数量级。 ## 九、扩展用法 这套方案不只适合做 Lottie 演示视频,常见的扩展场景: | 场景 | 改动 | |------|------| | 产品宣传片 | 把 Lottie 换成产品截图 + GSAP 入场动画 | | 教学录屏 | 在主时间线加 `<video>` 标签嵌入录屏文件 | | 数据可视化 | 用 GSAP + SVG 画图表,每秒渲染一帧 | | 字幕视频 | `<audio>` + GSAP 时间线同步字幕条 | 本质上,HyperFrames 把"浏览器 = 视频编辑器"这件事做实了。HTML/CSS/Lottie 写什么,渲染出来就是什么,没有中间格式损耗。 ## 写在最后 把脑洞变成 18 秒视频,传统流程要 After Effects + 视频剪辑软件 + 渲染农场。这套方案用 8 个 HTML 文件 + 1 个 npm 命令就能跑出 1080p 高清视频,整个过程代码可版本控制、可复用、可改参数即时预览。 Lottie + HyperFrames 这套组合最大的价值是**动画资产可复用**——同一个 `checkmark.json` 既能嵌在网页里做交互效果,又能直接渲染进视频做开场动画。设计师做一次,开发用两次,效率翻倍。 **你用 Lottie 做过最复杂的动画是什么?有没有想过把它直接渲染成视频?评论区聊聊你的脑洞。**
摘要
标签
多个标签用逗号分隔
分类
技术文章
教程指南
工具测评
项目实战
行业观察
默认
💾 保存修改
← 返回查看
返回列表