HyperFrames Camera Journey 蓝皮书:用单一虚拟摄像机状态机编排多程叙事动画
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
Camera Journey(摄像机旅程)是 HyperFrames 动画技能库(skills/hyperframes-animation)中定义的一种多阶段镜头叙事蓝图:让真正的视口摄像机充当"讲故事的人",在一个连续的世界里完成"推入 → 中途节拍触发 → 飞往结果/重新定位 → 落地推进"的多程旅程,并且旅程本身承载叙事。本文以 camera-journey.md 为骨架,结合底层摄像机规则(viewport-change、multi-phase-camera、coordinate-target-zoom、3d-camera-flight等)的源码级实现,完整讲解两种子形态(A:动作往返;B:无光标电影飞行)的镜头结构、运动词汇、规则映射、缓动语法与可寻帧安全约束,让你可以直接照此搭建一条"动作发生在这里、回报出现在那里"的摄像机叙事。
一、什么是 Camera Journey:意图与定位
在 HyperFrames 中,动画知识被组织为**原子规则(rules)与多阶段场景蓝图(blueprints)**两层。绝大多数场景只需从rules-index.md里挑选 2~4 条规则,用一条暂停的 GSAP 时间线拼装即可(见 SKILL.md);而camera-journey属于预先设计好的多阶段镜头模板,适用场景是"摄像机本身就是演员"的复杂编排。
核心意图(intent):真实的视口摄像机是讲述者——一条多程旅程(dive in 推入 → 中途节拍触发 → travel to the consequence / reposition 飞往结果或重新定位 → landing push 落地推进,最后静止),贯穿一个连续的世界,旅程本身承载叙事。它折叠出两种子形态:
- 形态 A:动作往返(action roundtrip)——摄像机推入某个 UI 面板,一个光标/输入动作被触发,然后摄像机 swoop/pan 到另一个区域,结果以元素运动的形式渲染出来;
- 形态 B:无光标飞行(cursorless flight)——纯粹的电影感 3D 飞行(运动模糊、景深、tilt-to-flatten 旋转),飞越静态或自动画内容,全程不出现光标。
边界(boundary):蓝图文档明确区分了三个相邻模板,避免误用:
| 模板 | 区别 |
|---|---|
cursor-ui-demo | 摄像机追逐光标(像伺服机构跟随演员);Camera Journey 中摄像机就是演员,按自己的叙事动机移动。形态 A 中光标只在"腿"(leg)的铰链处起作用,形态 B 中根本不出现 |
device-surface-showcase | 某个设备/表面是主角,摄像机只是呈现它;Camera Journey 中没有任何单一表面是主角,旅程穿越多个区域/面板/景深平面,穿越本身就是故事 |
spatial-pan-stations | 在平面画布上预置站点,用同类 pan 反复访问;Camera Journey 的每一程是异质的(push-in、swoop、pull-back-rotate、whip、dive),且每一程都是有动机的(被某个动作触发,或被它将要落地的揭示所驱动) |
对应三个相邻模板的完整配方分别见 cursor-ui-demo.md、device-surface-showcase.md、spatial-pan-stations.md。
二、适用场景:两种叙事角色
蓝图按两种"role"(在 HyperFrames 的创作流程中,role 指这条镜头要服务的叙事目标)给出应用入口:
Benefits(因果往返)——来自 briefcamera-swoop-panel-action-roundtrip:当收益本身就是一次"因 → 果"往返时使用,例如"在这里做一件小事,在那里得到一件大事":评论发出 → 图表变形;Agent 发现 → 验证过的 commit;聊天消息 → 收据 + 账本。摄像机的移动物理性地把动作和回报连接起来,让观众"旅行"过价值链条,而不是被告知这条链条。
Key_Feature(电影感关键特性)——来自 briefcursorless-camera-flight:当特性应该显得电影感且"不可避免"时使用——例如一个发薪表单,或一个由飞行摄像机探索的内容计划日历(dive、whip sweep、tilt-to-flatten、对 CTA/英雄卡片的暴力最终推进),内容自己表演(下拉框自选、关键词卡片只以景深方式存在),全程"无人掌舵"。
三、时长预算与总体镜头结构
时长(duration):5.6–11.1 秒。形态 A 为 5.6–9.0s(参考样例 001 为 5.6s、066 为 8.6s、004 为 9.0s);形态 B 为 6.3–11.1s(参考样例 Outrank 为 6.3s、094 为 11.1s)。
镜头结构(shot structure):整个场景是一个超大号的[world]——
- 形态 A:
[UI canvas:设计工具 / GitHub + Agent 面板 / 手机 + 桌面账本] - 形态 B:
[3D 布局空间:浮动表单卡片 / 带竖立关键词卡片的日历网格]
由一个单一虚拟摄像机包裹;内容作为世界内部的元素运动,摄像机负责旅行;每一程(leg)都是对同一个摄像机状态的顺序 tween。这一"单状态摄像机"正是 viewport-change.md 定义的基础原语:一个.world包裹层,一个cam { scale, x, y }状态——每一程的唯一事实来源(single source of truth)。
const cam = { scale: 1, x: 0, y: 0 }; function applyCamera() { world.style.transform = `translate(${cam.x}px, ${cam.y}px) scale(${cam.scale})`; } applyCamera(); // 播种第 0 帧:seek 到 t=0 时渲染开场姿态注意两个公式的区别(详见 viewport-change.md 与 coordinate-target-zoom.md):
- 单包裹层复合变换(本蓝图默认):
translate(x, y) scale(S)写在同一元素上,CSS 先应用 scale 再应用 translate,因此瞄准视口中心的反向平移为T = -offset × S; - 双层嵌套变换(
coordinate-target-zoom):外层 scale、内层 translate,公式为T = -offset(与 S 无关)。
两套公式不可互换——混用会让目标在变焦过程中漂离中心。
四、四程镜头逐程拆解(Scene 0–4)
Scene 0(可选,0.0–约 1.8s)——静态序幕
摄像机锁定在[序幕节拍:带浮动 3D 产品卡片的静态宣传卡 / 带强调词的打字标题 / 应用的宽景建立镜头]。打字机行可以在此完成([headline]逐个打出,强调词用[accent color])。
序幕通过硬切或标题缩小并滑走来打破,随后第一程 dive 开始——静默存在的意义,是让旅程的启动"落地"有力。打字机效果可选用 gsap-effects.md 的 Typewriter 配方(基于 GSAP TextPlugin),光标闪烁必须由时间线上的确定性 sin 方波驱动,而不是 CSS animation。
Scene 1(约 0.5–2.0s)——LEG 1:dive in 推入
摄像机快速、紧密地推入[焦点元素]:
- 形态 A:对整个视口的扁平 push,推向
[可操作元素:评论框 / Agent 面板 / 聊天气泡],[typed text]打完字或[response text]流入。页头/上下文离开画面——这是承诺,不是礼貌的缩放。 - 形态 B:push 以一个角度落地——
[表单区域 / 日历网格]的倾斜 3D 特写;旅行中前景元素运动模糊,邻居在景深下变软。一个巨大的[前景道具:日期数字 / 字段标签]可以占满画面并被速度模糊。
技术要点:紧密瞄准偏离中心的元素走coordinate-target-zoom(scale + counter-translate;测量目标而不是手推公式——旅程会在每一程放大对中误差)。目标偏移必须在 setup 阶段(await document.fonts.ready之后)测量一次并烘焙,绝不在onUpdate里调getBoundingClientRect。同时用头部空间预算限制峰值 scale(目标在峰值时不超过画布约 88%,否则check会报primary-offscreen)。
Scene 2(约 1.5–6.0s)——LEG 2:中途节拍(铰链)
摄像机保持、漂移或缓慢后拉,同时内容开始"表演":
- 形态 A —— 动作触发:
[光标]点击[Send / Create PR](或[message]隐式发送),被作用的元素清空/消失。点击前的可选戏剧化:[状态 spinner]循环[状态词]、[待办项]逐个划掉、[response text]流入。点击就是铰链,为下一程提供动机。 - 形态 B —— 内容自演:
[下拉框]自行展开(把[下方字段]推下去),无光标地显示[行悬停高亮],选中新值后收起;或者飞行减速进入对焦——[一个卡片]的[指标]锐利,邻居卡片模糊。
光标点击走 cursor-click-ripple.md(移动 → 按压下沉 → 波纹)+ press-release-spring.md(线性压缩 + 弹簧回弹;光标与目标同步下沉,光标压缩更多:0.80–0.90 vs 0.92–0.97);需要更"重"的按压感用 physics-press-reaction.md。被点击元素清空/消失,面板从状态 A 到 B,用 scale-swap-transition.md(同尺寸状态交换的缩小退出 + 弹入)或 card-morph-anchor.md(容器形态变化的视觉锚点)。下拉框自展开走 anchored-layout-expand.md(绝不 tweenheight,用掩膜 + 单 tween 同时驱动 sheet 与#below的位移)+ reactive-displacement.md(展开 tween 驱动下方兄弟元素的位移,二者共享同一个 tween 保持接触边永不分离)。
Scene 3(约 4.0–8.0s)——LEG 3:飞往结果 / 重新定位
- 形态 A:摄像机后拉 / swoop / pan到
[区域 B],同时结果以元素运动逐步构建——[柱子缩进基线,同时一条节点虚线从左向右画出 / 一条已验证的 commit 行滑入时间线并弹出反应 pill / 收据卡片从骨架逐行展开]。可选第二程延长旅程:[向右上 pan 到工具栏 → 下拉框级联展开 / 匹配剪辑进入极近特写 → 快速减速 zoom-out 揭示账本表格]。 - 形态 B:重新定位动作——缓慢后拉并把世界旋转放平居中(3D → 正对 2D),或一次重度运动模糊的WHIP SWEEP落到
[月历 / 整张卡片]的扁平横向 pan 上。扁平停顿时可以播放安静的元素节拍:一条细[focus 轮廓]淡入[某个字段]并向下扫到下一个;卡片保持几乎不可察觉的倾斜/缩放漂移,让停顿永不"死"。
柱→线图变形是一个干净的组合,无需新规则:stat-bars-and-fills(柱子scaleY缩进基线,stat-bars-and-fills.md)+svg-path-draw(节点虚线用stroke-dasharray/stroke-dashoffset左→右画出,路径长度用getTotalLength()实测,svg-path-draw.md)放在同一时间线位置。commit 行滑入 / 反应 pill 弹出 / 收据逐行展开:单个到达用 spring-pop-entrance.md,逐行级联用 waterfall-entry.md(opacity 走二进制的tl.set,绝不淡入淡出)。focus 轮廓在字段间移动,把 ai-tracking-box.md 重排成普通轮廓即可(偏移在 setup 烘焙,尺寸用 scale 变形,绝不 tween width/height)。whip sweep 由 nudge-curve.md 的三段链(power3.inramp-in ≈10% / linear burst ≈65% /power4.outtail ≈25%,tail 时长 ≥ ramp-in 的 3 倍)+ motion-blur-streak.md 的 Camera-Travel 豁免在同一窗口组成:
// whip sweep:位置走 nudge-curve 三段链,模糊信封同窗跟随 tl.to(cam, { x: WHIP_X * 0.1, duration: 0.12, ease: "power3.in", onUpdate: applyCamera }, WHIP_AT); tl.to(cam, { x: WHIP_X * 0.75, duration: 0.1, ease: "none", onUpdate: applyCamera }, WHIP_AT + 0.12); tl.to(cam, { x: WHIP_X, duration: 0.35, ease: "power4.out", onUpdate: applyCamera }, WHIP_AT + 0.22); // 模糊:ramp-in 内升到 PEAK,burst 期间保持(匀速 = 恒定的拖影),tail 内归零 tl.to(blurProxy, { v: PEAK_BLUR, duration: 0.12, ease: "power3.in", onUpdate: writeBlur }, WHIP_AT); tl.to(blurProxy, { v: 0, duration: 0.35, ease: "power4.out", onUpdate: writeBlur }, WHIP_AT + 0.22);新区域的内容要在 burst 期间完成切换/揭示——拖影掩盖变化,power4.out尾段把它锐利地落地。
Scene 4(最后约 1–2s)——LEG 4:落地
旅程在回报上收束:
- 形态 A:摄像机静止;
[光标]悬停或漂向[回报:展开的 Export 菜单项 / commit 链接 / View-transaction 按钮];结束于静止、变化后的状态——世界与旅程开始处明显不同。 - 形态 B:一次突然的暴力 push-in/dive(运动模糊)冲向
[被放大到占满画面的 CTA 按钮 / 英雄关键词卡片],结束时紧握——或停在 dive 中途(最后几帧仍在旅行,扁平全景明确不是最终画面)。
打字、流式 AI 回复、状态词 spinner、骨架→内容切换统一走 discrete-text-sequence.md(稀疏{t, text}状态数组 + 每帧反向搜索;打字机修饰可叠 gsap-effects.md,输入框光标用 context-sensitive-cursor.md);各程出现哪些内容、收据行与发现按脚本窗口到达,用 dynamic-content-sequencing.md(按内容长度自动计算每条目的起止时间,绝不硬编码偏移)。待办划掉、行悬停高亮走 css-marker-patterns.md(strike-through)与 asr-keyword-glow.md(悬停/选中行的强调光晕)。卡片微浮、停顿上几乎不可察觉的倾斜/缩放漂移走 sine-wave-loop.md(含multi-phase-camera的 drift 侧微运动;倾斜分量属于3d-camera-flight)。
五、运动词汇表(motion vocabulary)
蓝图把该主题的全部可用运动词汇列成清单,写作时按需取用:
整视口推入(快而紧 / 慢而微妙);后拉重取景;向上/右/下 pan;叠层面板间的 dive/swoop;快速减速 zoom-out 落定;对放大成巨幅的按钮的突然暴力推入;穿越卡片网格的连续 3D 飞行;dive 进倾斜 3D 特写;把世界旋转放平到正对的慢速后拉;重度运动模糊的 whip sweep;摄像机旅行的运动模糊;带模糊邻居的景深;减速入焦;硬切 / 匹配剪辑进极近特写;停顿上的近不可察觉倾斜/缩放漂移;输入框内打字完成;打字机标题;标题在 dive 时缩小滑走;流式 AI 回复文本;状态词 spinner 循环;待办划掉绘制;光标点击;被点击元素清空/消失;下拉框级联展开 / 自展开并带行悬停高亮收起(推动下方字段);柱→线图变形(柱子缩进基线、节点虚线左→右画出、标签保持);时间线上 commit 行滑入;反应 pill 出现;骨架→内容卡片构建;收据/标签行逐行展开;细 focus 轮廓淡入并在字段间扫动;摄像机向按钮漂移;3D 卡片微妙浮动;静止时光标悬停。
六、规则映射总表(rule mapping)
蓝图将每个叙事节拍映射到底层规则。下表按主题归组(映射原文见 camera-journey.md):
| 节拍 | 规则 |
|---|---|
| 多程摄像机本身(顺序 push / pull-back / pan / dive 相位 + 保持活力的微漂移) | multi-phase-camera(相位排序 + drift)叠加在viewport-change(基础原语:单一.world包裹层、一个cam状态)之上 |
| 紧密 dive 到偏离中心元素 | coordinate-target-zoom(scale + 反向平移;测量目标,不手推) |
| 极近特写快速减速 zoom-out 落定(066 账本揭示) | coordinate-target-zoomzoom-out 变体 /multi-phase-camera(pull 相位、硬power4.out) |
| 摄像机旅行运动模糊(dive、whip、暴力最终推) | motion-blur-streakCamera-Travel 豁免(模糊信封骑在.world包裹层上,峰值速度处达到峰值,每次落地解析锐利) |
| 邻居模糊 + 单卡片入焦;减速入焦 | depth-of-field-blur(focal pull 与 blur-the-cluster-while-pushing-in 均明确在范围内;DoF tween 与摄像机 leg 同位置同窗) |
| 3D 飞行本身(形态 B 核心) | 3d-camera-flight(perspective 包裹层 + preserve-3d;扁平 leg 仍归 2D 摄像机规则管) |
| whip sweep | nudge-curve(burst 主导调校的慢-快-慢滑动)+motion-blur-streak(摄像机旅行豁免)同窗 |
| 输入框打字完成、打字机标题、流式回复、spinner、骨架→内容 | discrete-text-sequence(+gsap-effectstypewriter;输入框光标用context-sensitive-cursor) |
| 每程内容 / 收据行与发现按脚本窗口到达 | dynamic-content-sequencing |
点击[Send / Create PR](形态 A 铰链) | cursor-click-ripple+press-release-spring(更重的按压用physics-press-reaction) |
| 被点击元素清空/消失;返回程面板状态 A→B | scale-swap-transition/card-morph-anchor |
| 待办划掉;行悬停高亮 | css-marker-patterns(strike-through)·asr-keyword-glow(强调光晕) |
| 柱→线图变形 | 组合分解:stat-bars-and-fills(柱scaleY→ 基线)+svg-path-draw(节点虚线左→右画出)同一时间线位置——无单一规则命名该协调变形,也无需新规则 |
| commit 行滑入;reaction pill 出现;收据逐行展开 | spring-pop-entrance(单个到达)/waterfall-entry(逐行级联) |
| 下拉框自展开并位移下方字段(094) | anchored-layout-expand(掩膜边缘锚定展开——绝不 tweenheight)+reactive-displacement(展开 tween 驱动兄弟位移) |
字段间 focus 轮廓移动(094:细轮廓在From淡入,扫到Amount) | ai-tracking-box重排成普通轮廓(偏移 setup 烘焙;尺寸用 scale 变形) |
| 3D 卡片微浮;停顿近不可察觉倾斜/缩放漂移 | sine-wave-loop(+multi-phase-camera的 drift;倾斜分量归3d-camera-flight) |
| 摄像机向按钮漂移;停顿上的慢速微妙 zoom-in | multi-phase-camera(steady-push 模式、极小 spread) |
七、摄像机语法(camera grammar):这个蓝图就是它的摄像机
蓝图明确指出:"这个蓝图就是它的摄像机"。它定义了一层严格的摄像机语法:
- 每一程都是对一个摄像机状态的 tween——
viewport-change的单一.world包裹层 /cam对象,由multi-phase-camera排序,由coordinate-target-zoom瞄准。 - 每程必须有动机:形态 A 因为动作触发而移动(点击 → swoop 到结果);形态 B 因为下一个揭示需要而移动(dive → focus → reposition → final dive)。
- 变化程的动词——四段一模一样的 push 读起来像幻灯片。dive、swoop、pull-back-rotate、whip、pan 交替使用。
- 缓动法则(ease law):dive 与落地用硬
out族(power4.out——暴烈到达、锐利落定);重新定位程用power2.inOut;摄像机上的 spring/back 缓动是错的(世界会像挂在绳子上一样晃动)。形态 B 在同一个单状态纪律下叠3d-camera-flight的 perspective 包裹层。
3D 飞行(形态 B)的层次结构(详见 3d-camera-flight.md),五层严格分离:
- 镜头——静态
.stage上的perspective: PERSPECTIVE_PX(700–1400px,移动摄像机最佳 800–1200),设置一次、永不 tween; - 世界——
transform-style: preserve-3d的.world,按最终 1× 尺寸布局:地面表面为扁平 DOM,可选道具以静态translateZ(PROP_Z)(80–300px)偏移制造视差,竖立卡片反向倾斜面向摄像机; - 摄像机状态——单一对象
cam = { x, y, z, rx, ry },由唯一的applyCamera()以固定顺序写入:translate3d(x, y, z) rotateX(rx) rotateY(ry)——translate 在旋转之外,所以任意倾斜下x/y/z始终沿屏幕轴移动; - 程——对
cam的顺序 tween:dive in(power4.out)、tilt-to-flatten 后拉(power2.inOut,这是重定位不是撞击)、横向飞行、最终 dive; - 深度线索——非焦点平面走
depth-of-field-blur的--doftween(叶子元素,绝不作用于世界本身);旅行模糊走motion-blur-streak豁免,作用于.stage——对preserve-3d元素加filter会把它压扁(transform-style: flat),坍缩所有translateZ。
3D 的落地姿态是设计并烘焙的,不是推导的:设计期给cam设候选值、调applyCamera()、截图、调整、烘焙成常量。3D 里没有容易算错的反向平移公式——姿态本身就是设计决策。注意 Z 预算:cam.z + PROP_Z ≤ ~0.6 × PERSPECTIVE_PX内容才可读,只有可牺牲的道具可以穿过摄像机平面(fly-past)。
景深(DoF)参数(depth-of-field-blur.md):每深度步 3–6px 模糊(BLUR_PER_DEPTH),完全失焦平面终端 8–24px(MAX_BLUR),同时把非焦点层opacity降到 0.4–0.7 的DIM_LEVEL——小半径 + 变暗比把模糊拉满更像真景深。focal 层必须z-index高于模糊层,且--dof保持 0。DoF 与摄像机相互独立:模糊层、变换.world,不要用摄像机变换假装景深。
运动模糊峰值(motion-blur-streak.md):整帧包裹层上限约 18–20px(单元素可达 30);横向 whip/pan 用"X 0"轴向,dive/push 用"B B";位置与模糊信封必须共享 ease 和窗口——模糊在峰值速度处达峰、落定瞬间归零,残留的模糊读起来像"跑焦"。
八、可寻帧安全(seek-safety):镜头量大的非谈判项
HyperFrames 的渲染契约是单一暂停时间线、确定性、可寻帧(见 SKILL.md 对hyperframes-core契约的引用)。对 Camera Journey 这种"摄像机本身在运动"的场景,蓝图规定:
- 整个旅程——每一程、每个模糊信封、每次 DoF 拉动——都活在同一条暂停的 GSAP 时间线上,任何一帧 seek 都能复现中途 leg 的精确摄像机姿态;
- 一个摄像机状态对象,变换由唯一写入者
applyCamera()组合;.world包裹层附近绝不能出现 CSStransition; - 模糊走proxy-tween 的属性 /
--dof变量(两者都可寻帧);结尾停在 dive 中途也没问题——seek 到最后一帧只是落在 tween 中途; - 每程目标在 setup 时测量一次(
fonts.ready之后)并烘焙;绝不在onUpdate里getBoundingClientRect(并行帧采样下会失同步,见 SKILL.md 的"预计算布局常量"约束); - 不得使用
Math.random/Date.now/performance.now、repeat: -1、对width/height/top/left的布局 tween(空间运动只用 GSAP 变换别名x/y/scale/rotation)。
编排完成后,可以用技能自带的动画地图审计脚本检查节拍与生命周期:node skills/hyperframes-animation/scripts/animation-map.mjs <composition-dir> --out <composition-dir>/.hyperframes/anim-map(它会枚举window.__timelines上的全部 tween 并输出animation-map.json,见 SKILL.md)。
九、Overflow 处理:让check通过的必要条件
旅行摄像机故意让世界内容在每一程越过画面边缘,这是设计使然。为了让check干净通过:
- 场景根节点必须保留
overflow: hidden(否则任何非 1.0 的 scale 都会露出边缘,见 viewport-change.md 的约束); - 移动的
.world包裹层必须标记data-layout-allow-overflow——否则check会对旅程"遗弃"的每个面板上报text_box_overflow/container_overflow。同样的说明也出现在 device-surface-showcase.md 与 3d-camera-flight.md 中(3D 飞行要求overflow: hidden在.scene+data-layout-allow-overflow在.world成对出现)。 - 背景色放在
.scene,绝不放在.world——世界携带背景会随摄像机变换扭曲,并在缩放/平移时暴露外层空洞。
十、落地检查清单
在真正写 composition 之前,用下面这份从蓝图与规则提炼的清单做一次自查:
- 单摄像机纪律:整个场景一个
.world(或 3D 的.stage+.world),一个cam状态,一个applyCamera()写入者;drift 也通过同一写入者叠加,绝不让 drift 自己写world.style.transform; - 公式匹配包裹层结构:单层用
T = -offset × S,双层嵌套用T = -offset,不混用; - 四程动词异质且有动机:dive → hinge → travel → landing,每程回答"为什么现在动";
- 缓动法则:dive/落地
power4.out,重定位power2.inOut,杜绝 spring/back 于摄像机; - 测量一次、烘焙到底:目标偏移、
BODY_H、路径长度等全部 setup 期确定;fonts.ready之后再测量; - DoF 与模糊可寻帧:
--dof变量与 proxy-tween 的属性,与 leg 同窗同 ease; overflow: hidden+data-layout-allow-overflow成对出现,背景在.scene;- 时长匹配:总时长落在 5.6–11.1s 区间,并让最终 dwell ≥ 1s(除非刻意停在 dive 中途)。
Camera Journey 的完整原始配方位于 camera-journey.md,全部底层规则在skills/hyperframes-animation/rules/下按名可查,示例代码(如多短语顺序 messaging-multi-phrase.html、点击波纹参考 cta-orbit-collapse.html)可直接对照复刻。
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考