📄 文档管理系统

← 返回列表

pi + HyperFrames + Lottie 三件套实战:我把一个脑洞做成了 18 秒的 1080p 演示视频

article #pi #HyperFrames #Lottie #GSAP #视频生成 📅 创建:2026-06-05 07:24:08 🔄 更新:2026-06-04 23:24:41
👁️ 预览 & 复制到公众号 ✏️ 编辑

最近在用 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 集成规范

这一步是整个项目最关键的技术点,写错了就白干。

核心模式

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

const tl = gsap.timeline({ paused: true });
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);

Scene 4 打字机实现(每个字符包 <span class="ch">,空格替换为 &nbsp;):

const text = "npx --yes hyperframes@0.6.56";
const container = document.querySelector(".typed-text");
container.innerHTML = text.split("").map(c => 
  c === " " ? '<span class="ch">&nbsp;</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 秒一闪):

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

@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

解决:用有限计数公式:

// 视频总时长 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 模拟光晕:

.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:

.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 renderEEXIST 错。

解决

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 秒加个静音但有字幕的过渡

最后我把视频时长和音频对齐了。

七、渲染命令

整个项目最关键的一行命令:

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,但文件大小会翻倍。

完整流程

# 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 做过最复杂的动画是什么?有没有想过把它直接渲染成视频?评论区聊聊你的脑洞。

💬 评论区

加载中...