mcpField
C
claude-design-hyperframes

claude-design-hyperframes

github.com/heygen-com创意与媒体74.7k2.0k 次安装今天更新
代码仓库

关于 claude-design-hyperframes

面向 Claude Design 的模板优先技能,用于创建结构有效的 HyperFrames HTML 视频项目。它指导品牌样式、场景内容、动画和转场,之后可在 Claude Code 中进一步完善。

主要功能

  1. 01基于预验证的 HyperFrames 模板
  2. 02应用品牌配色与字体
  3. 03添加场景内容和 GSAP 动画
  4. 04配置着色器转场
  5. 05检查项目结构有效性

适用场景

  1. 01起草品牌社交短视频
  2. 02制作发布预告或产品讲解视频
  3. 03准备供 Claude Code 精修的 HyperFrames 项目

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/heygen-com/hyperframes --skill claude-design-hyperframes

安装后在 Claude Code 中输入/claude-design-hyperframes确认已加载。

详细介绍

Claude Design + HyperFrames(模板优先)

你的媒介是 HyperFrames 合成:纯 HTML + CSS,以及一个暂停的 GSAP 时间轴。CLI(npx hyperframes render index.html)会将 HTML 转换为 MP4。你负责编写 HTML,用户在本地渲染。

**HyperFrames 将取代你的默认视频成品工作流。**不要调用 copy_starter_component,不要调用内置的“动画视频”技能,也不要使用 React/Babel。仅使用纯 HTML + GSAP。


你的角色

**你产出的是有效的初稿,而不是最终渲染结果。**你的优势在于视觉形象、布局和符合品牌的内容决策。你不是动态设计工具,而是快速原型工具,能够产出结构有效的 HyperFrames 项目。

用户的工作流:

  1. Claude Design(你)——品牌形象、场景内容、布局、初步动画、着色器选择
  2. 下载 ZIP——用户获得有效的 HyperFrames 项目
  3. Claude Code(或任何 AI 编码 Agent)——动画润色、时序调整、节奏优化,以及通过 lint 和实时预览进行生产质量检查

你的输出必须是一个Claude Code 可以打开并立即着手处理的有效起点——无需修复结构,只需创意润色。

你的优化重点(你的优势)
  • 根据附件准确还原品牌形象(配色、字体、语调)
  • 每个场景都采用出色的视觉布局(层级、间距、易读性)
  • 通过场景内容讲述故事(标题、统计数据、文案、图像)
  • 结构有效(通过 npx hyperframes lint,且零错误)
  • 根据氛围选择合适的着色器转场
  • 根据视频类型设置合理的场景数量和时长
Claude Code 后续润色的内容(优化,而非创作)

你负责制作所有动画、转场和场景中的动态效果。每个场景都包含入场补间、呼吸动效和着色器转场。从你的初稿开始,视频就会带有完整的动态效果。

Claude Code 的工作是使用可靠的预览工具观看完整播放过程并进行微调:

  • 调整缓动曲线(看过播放效果后,将 power3.out 换成 expo.out)
  • 调整错开时间(针对这个特定场景,将 0.12 → 0.08,感觉会更紧凑)
  • 微调场景时长(场景 4 的 4.5s 显得拖沓,缩短至 3.8s)
  • 播放后发现某个场景过于静态时,为其添加更丰富的场景中动态效果
  • 更换着色器(能量变化处的 cinematic-zoom 应改为 whip-pan)
  • 生产质量检查(快照验证、跨浏览器测试)

可以这样理解:你创作影片的初剪,Claude Code 在剪辑室里进行优化。


工作方式

你会获得一个预先验证通过的骨架,它已通过 HyperFrames 检查器。你的工作:

  1. 阅读需求说明,选择一个骨架
  2. 填写配色和字体(CSS 自定义属性)
  3. 填写场景内容(在 .scene-content 内设置文本和布局)
  4. 填写 GSAP 动画(按场景标记的时间轴代码块)
  5. 验证预览,交付 ZIP

骨架会处理结构规则——数据属性、时间轴注册、HyperShader 接线、初始可见性、preview.html 令牌传递。你专注于创意工作。

**你可以修改的内容:**CSS 自定义属性、场景内容、动画补间、场景数量(按照下方规则添加或删除场景)、着色器选择、时长。

你不得修改的内容:<script> 加载顺序、window.__timelines 初始化、场景容器上的 .scene.clip 类、每个场景中的 .scene-content 包装元素、preview.html 结构。


第 1 步:理解需求说明

**检查项:**你能说出主题、时长、宽高比,以及至少一种视觉方向的来源。

输入信息(按可靠性排序)
  1. 附件(最可靠)——截图、PDF、品牌指南、参考图片。参考它们来确定配色、字体和语气。
  2. 粘贴的内容——十六进制颜色代码、字体、文案、脚本。
  3. 研究——使用 web_search 搜索品牌信息。静态页面(博客、新闻稿、维基百科)效果不错。SPA 首页只会返回空壳,请改查博客、新闻稿或维基百科。
  4. 用户提供的 URL——从这些链接开始,再向外扩展。
如果需求说明过于简略,只问一个问题

如果提示中没有以下任何内容:附件、十六进制颜色代码或指定字体、指定美学风格/导演、知名品牌,或“直接构建”/“给我惊喜”,请用具体选项提出一个简短的澄清问题。等待对方回复。


第 2 步:选择骨架并填充品牌特征

检查点: 已有一个可运行的 index.html,其中 :root 定义了配色和字体。预览可正常渲染(即使场景为空)。

根据视频类型选择
类型时长场景数骨架
社交短片(9:16)10-15s5-7骨架 A
发布预告片(16:9)15-25s7-10骨架 B
产品讲解视频(16:9)30-60s10-18骨架 C
电影感标题片(16:9)45-90s7-12骨架 D

复制骨架(见下方第 7 节),然后立即填充 :root CSS 自定义属性:

:root {
  /* === FILL: Your brand identity === */
  --bg: #0a0a0d;
  --ink: #f5f5f7;
  --accent: #7c6cff;
  --muted: #5a6270;
  --accent-dim: #3d3680;
  --font-display: "Space Grotesk", sans-serif;
  --font-data: "JetBrains Mono", monospace;
}
避免千篇一律

这些都是所有 LLM 都会优先选择的默认项。请选择真正符合需求说明的方案:

  • 禁用字体: Inter、Inter Tight、Roboto、Open Sans、Noto Sans、Lato、Poppins、Outfit、Sora、Fraunces、Playfair Display、Cormorant Garamond、EB Garamond、Syne、Cinzel、Prata、Bodoni Moda、Nunito、Source Sans、PT Sans、Arimo。
  • 禁用字体搭配: Fraunces + JetBrains Mono、Inter + 任意字体、Playfair + Lato。
  • 质疑这些默认项: 渐变文字、深色背景配青色、纯 #000/#fff、一模一样的卡片网格、左侧边缘强调条、所有内容都居中且权重相同。

选择一组风格鲜明的字体搭配。字重对比必须明显(300 对 900,而不是 400 对 700)。视频字号:标题至少 60px,正文至少 20px,标签至少 16px。


第 3 步:填充场景——内容 + 动画

检查点: 每个场景都有可见内容、至少 2 种第 8 节所述的动画模式,以及场景中段的动态效果。不能有静态场景。

逐个处理场景。每个场景:

3a. 填充场景内容

将文本、图像和布局放入 .scene-content 包装元素中。骨架中已包含该包装元素——请在其中添加元素。

<div class="scene-content">
  <h1 id="s3-title" class="display">$1.9 Trillion</h1>
  <p id="s3-sub" class="body-text">processed annually</p>
  <div id="s3-bar-chart">...</div>
</div>

将装饰元素(光效、颗粒、暗角)放在 .scene-content 外部,直接放入场景 div 中。

3b. 填充入场动画

在标记为当前场景的时间轴代码块中,添加 tl.from() 补间动画。将动画对象从屏幕外/不可见状态变为 CSS 中的位置:

// === SCENE 3 ===
tl.from("#s3-title", { y: 40, autoAlpha: 0, duration: 0.6, ease: "power3.out" }, 10.3);
tl.from("#s3-sub", { y: 20, autoAlpha: 0, duration: 0.5, ease: "power2.out" }, 10.7);
tl.from(
  "#s3-bar-chart",
  { scaleY: 0, transformOrigin: "bottom", duration: 0.8, ease: "expo.out" },
  11.0,
);

将第一个补间动画的开始时间延后 0.1-0.3s。延迟为零的入场动画会让人感觉像跳切。

3c. 填充场景中的动态效果(这是视频区别于幻灯片的关键)

每个可见元素在入场后都必须继续运动。静态背景上的静态元素就像一张带进度条的 JPEG。每个场景至少使用第 8 节中的 2 种模式。

元素场景中的动态效果第 8 节中的模式
统计数据 / 数字计数器从 0 动画递增至目标值计数器动画
SVG 线条 / 路径实时绘制自身SVG 描边绘制
标题 / 字标字符逐个入场字符交错入场
Logo / 标识组合轻微上下漂移呼吸式浮动
图表 / 条形图条形依次填充条形图填充
图像 / 截图缓慢缩放:scale: 1 -> 1.03Ken Burns(直接使用 tl.to(el, { scale: 1.03, duration: sceneLength, ease: "none" }))
强调色 / 高亮扫过文字高亮扫过
背景光晕不透明度脉冲变化tl.to(".glow", { opacity: 0.6, duration: 1.5, ease: "sine.inOut", yoyo: true, repeat: 1 })

**每个场景的最低要求:**入场补间动画 + 至少一种连续动态效果(浮动、计数、缩放或光晕)。包含统计数据或图表的场景应始终使用计数器或条形填充模式——这些效果最具视觉吸引力,也最容易实现。

3d. 调整场景时长

骨架中的时长是占位值。请根据以下内容调整每个场景的 data-duration:

  • **阅读时间:**统计显示文本的字数,并参考下方的时长预算
  • 最后一个可读元素必须在场景时长过半前完成入场
显示文本最短时长
无文本(主视觉、图标)1.5-2s
1-3 个词(引导语、数字)2-3s
4-10 个词(标题 + 副标题)3-4s
11-20 个词(一两句话)4-6s
21-35 个词(一段文字)6-8s
35 个词以上拆分为两个场景

硬性上限:每个场景 5s,除非你能说明具体原因(主视觉停留、电影感推镜、较长的计数器动画)。

更改场景时长时,请更新后续场景的 data-start,以保持场景首尾相接。还要更新根元素的 data-duration,使其与总时长一致。

调整缓动效果

每个场景至少使用 3 种不同的缓动效果。不要默认所有动画都使用 power2.out。

感觉缓动效果时长
平滑power2.out0.4-0.6s
利落power4.out0.2-0.3s
弹跳back.out(1.6)0.3-0.5s
戏剧性expo.out0.3-0.5s
梦幻sine.inOut0.5-0.8s
机械steps(5)0.3-0.5s

第 4 步:转场

专业规则:大多数切换都采用硬切

在专业视频中,约 95% 的场景切换都是硬切。特效转场(着色器、叠化)仅用于 2-3 个关键时刻——主角亮相、能量转变、CTA 落点。每次切换都用着色器,就像把一段文字里的每个词都加粗一样。

骨架预先设置了在关键时刻使用 2 个着色器转场,其他地方全部硬切。这样节奏会更丰富:切-切-着色器-切-切-着色器-切。

三种转场类型

硬切(默认——大多数场景都使用): 无需转场代码。场景 N 消失,场景 N+1 出现。新场景的入场动画负责所有视觉效果。这是专业视频的默认做法。

着色器转场(每个视频使用 2-3 次——用于主角亮相/高潮/CTA 时刻): 骨架已在关键位置预先设置。HyperShader 会将两个场景捕获为纹理,并通过 WebGL 逐像素合成。

何时使用着色器,何时使用硬切:

使用着色器的场景使用硬切的场景
主角亮相 / 产品揭晓功能之间的衔接场景
能量显著转变或章节转换快速呈现的列表或统计数据
CTA / 最后的品牌时刻连续 3 个以上快速场景切换
音乐强调的任何时刻需要营造快速节奏感的场景

经验法则:6-8 个场景的视频适合使用2 个着色器转场,其余全部硬切。

调整着色器转场

更改着色器名称——从以下 14 个名称中选择:

domain-warp、ridged-burn、whip-pan、sdf-iris、ripple-waves、gravitational-lens、cinematic-zoom、chromatic-split、swirl-vortex、thermal-distortion、flash-through-white、cross-warp-morph、light-leak、glitch

根据能量匹配着色器:

能量着色器
平静、编辑感cross-warp-morph、light-leak、domain-warp
中等、专业cinematic-zoom、whip-pan、sdf-iris
高强度、激进glitch、chromatic-split、ridged-burn
空灵、神秘gravitational-lens、ripple-waves、swirl-vortex

调整转场时长——更改场景时长后,重新计算每个转场的 time:

transition.time = scene_boundary - (transition.duration / 2)

示例:scene-3 在 8s 结束,转场时长为 0.5s -> time: 7.75。

转场时长不得少于 0.3s。 最佳时长为 0.5s。

骨架如何处理这些情况

骨架只在 HyperShader.init() 中列出锚点场景(即衔接着着色器转场的场景)。锚点场景使用 style="opacity:0;",因为 HyperShader 会管理它们的不透明度。非锚点场景使用 style="visibility:hidden;"。

关键——如果不处理,以下两个错误会导致“中间场景不可见”:

  1. 非锚点场景需要显式设置 tl.set 可见性切换。 如果不设置,场景容器会保持 visibility:hidden,子动画则会在不可见的父级中播放。

  2. 每个着色器组中的第一个锚点场景需要设置 tl.set("#sN", { opacity: 1 }, <start-time>)。 HyperShader 浏览器模式不会自动显示第一个锚点。它在整个时间窗口内都会保持 opacity:0。每个 demov4 合成都会出现这个错误。

骨架会为每个非锚点场景预先设置这些切换,使用的是 autoAlpha(而非 visibility):

// --- Non-anchor scene toggles (REQUIRED — must use autoAlpha, not visibility) ---
tl.set("#s1", { autoAlpha: 0 }, 2.5); // hide s1 at its end time
tl.set("#s2", { autoAlpha: 1 }, 2.5); // show s2 at its start
tl.set("#s2", { autoAlpha: 0 }, 5.0); // hide s2 at its end
tl.set("#s3", { autoAlpha: 1 }, 5.0); // show s3 at its start
tl.set("#s3", { autoAlpha: 0 }, 7.5); // hide s3 at its end

为什么用 autoAlpha,而不是 visibility: 每当触发着色器转场时,HyperShader 都会将所有 .scene 元素设为 opacity:0。如果非锚点场景只切换 visibility,这个全局重置就会影响它的 opacity——场景会变为 visibility:visible,但 opacity:0(仍不可见)。autoAlpha 会在一次调用中同时设置 opacity 和 visibility,覆盖这个全局重置。

规则:

  • 每个非锚点场景都要添加 tl.set("#sN", { autoAlpha: 1 }, <data-start>) 和 tl.set("#sN", { autoAlpha: 0 }, <data-start + data-duration>)
  • 场景 1 只需在结束时间设置隐藏(它开始时可见)
  • 锚点场景不要添加 autoAlpha 切换——它们的不透明度由 HyperShader 管理
  • 添加或删除场景时,相应更新这些切换

8 场景视频的示例,其中着色器转场位于 s4→s5 和 s7→s8:

  • 锚点场景:s4、s5、s7、s8(列在 HyperShader 的 scenes 数组中,使用 opacity:0)
  • 非锚点场景:s1、s2、s3、s6(不在 HyperShader 中,使用 visibility:hidden,并显式添加 tl.set 切换)
  • 场景 1 没有内联样式(从 t=0 起可见)
添加或删除着色器转场

要在两个场景之间添加着色器转场:

  1. 将两个场景 ID 都添加到 HyperShader.init() 中的 scenes 数组
  2. 将转场对象添加到 transitions 数组
  3. 将两个场景的样式都从 visibility:hidden 改为 opacity:0
  4. 不变量:scenes.length === transitions.length + 1

要删除着色器转场(改为硬切):

  1. 从 scenes 中移除场景 ID(除非它们也是其他转场的锚点)
  2. 从 transitions 中移除该转场
  3. 将受影响场景的样式从 opacity:0 改为 visibility:hidden

禁止:不可见的桥接转场。 绝不要用 0.01s 的 flash-through-white 填充。


步骤 5:验证预览并交付

验收标准: 预览从头到尾都能播放。所有场景均可见。没有闪烁。文字清晰可读。

在预览窗格中验证

逐一检查每个场景:

  1. 场景 1 是否立即出现?(如果是黑屏:运行时未加载,或 __timelines 键不匹配)
  2. 着色器转场是否流畅触发?(如果闪烁:转场太短,或退出动画先于转场发生)
  3. 所有文字在背景上是否清晰可读?
  4. 每个场景在停留期间是否都有动态效果?(如果静止:缺少场景中段的动态效果)
  5. 动画是否按正确顺序播放?
故障排查:预览画面全黑
症状原因修复方法
全黑运行时脚本缺失或顺序错误检查 GSAP 是否在运行时脚本之前加载到 <head> 中
全黑__timelines 键与 data-composition-id 不匹配两者都必须是 "main"
全黑preview.html 中未转发令牌检查是否将 location.search 附加到 src
场景未出现data-start / data-duration 错误检查场景时间窗口是否首尾相接
转场前闪烁着色器触发前播放了退出动画移除退出补间动画——着色器本身就是退出效果
转场前闪烁转场时长 < 0.3s增加到 0.5s
向后定位时显示空白异步捕获竞态条件这是 HyperShader 浏览器模式中的已知错误。向前定位通常可正常工作。若要可靠地拖动预览,请下载并在本地运行 npx hyperframes preview
中间场景不可见未显示首个着色器锚点在每个着色器组的第一个锚点处添加 tl.set("#sN", { opacity: 1 }, startTime)
中间场景不可见非锚点使用了 visibility 而不是 autoAlpha改为 tl.set("#sN", { autoAlpha: 1 }, start) 和 tl.set("#sN", { autoAlpha: 0 }, end)。着色器的全局重置会破坏 opacity;仅使用 visibility 无法修复。
交付内容

请提供:index.html、preview.html、README.md 和 DESIGN.md。

preview.html 和 README.md 已包含在骨架中——不要修改 preview.html。根据你的 :root 自定义属性生成 DESIGN.md,作为参考文档。

在最终消息中告知用户:

  1. 构建内容——场景数量、时长、视觉风格概述、使用的着色器转场
  2. 下一步操作——下载 ZIP,并在本地运行 npx hyperframes preview,以可靠地播放完整合成内容
  3. 在 Claude Code 中完善的内容——具体说明哪些场景需要优化动画、哪些地方可以收紧节奏、哪些场景中的活动较基础且可以丰富。不要只说“在 Claude Code 中完善”——要说“场景 4 的计数器动画可以通过延长时长变得更流畅,场景 6 则可以为徽标增加轻柔的浮动效果。”
  4. 注意事项——占位素材、未经核实的统计数据、受真实品牌启发的元素

第 6 节:不可违反的规则

骨架代码会处理大多数结构规则。以下是骨架代码无法强制执行的运行时规则:

确定性(不可妥协)
绝不使用改用
Math.random()有种子的伪随机数生成器(仅在需要随机性时使用)
Date.now()、performance.now()硬编码时间,或在 onUpdate 中使用 tl.time()
setInterval、setTimeout时间线补间 + onUpdate
repeat: -1repeat: Math.ceil(duration / cycle) - 1
stagger: { from: "random" }from: "start"、"center"、"end"
异步构建时间线页面加载时同步构建
媒体规则
绝不使用改用
video.play()、audio.play()由框架控制播放
不带 muted 的 <video>始终使用 muted playsinline
<video> 上的音频单独的 <audio> 元素
Base64 媒体文件引用或 HTTPS URL
占位符 URL(placehold.co)真实素材
动画规则
绝不使用改用
着色器转场前的退出补间着色器本身就是退出效果——内容保持可见
对场景容器使用 tl.set / tl.to由 HyperShader 控制场景透明度
requestAnimationFrameGSAP 补间
选择器中的模板字面量硬编码字符串
使用 CSS transform 居中在包装元素上使用 Flexbox 居中
SVG 滤镜 data:image/svg+xml 颗粒效果CSS radial-gradient 颗粒效果(见下方模式)
对 visibility / display 做动画使用 autoAlpha
自查清单

交付前运行检查。依据实际代码检查,而不是凭假设。

结构有效性(必须通过——Claude Code 无法轻松修复这些问题):

  • 每个场景都有 class="scene clip" 和所有 data 属性
  • 每个场景都有 <div class="scene-content"> 包装元素
  • 锚点场景有 style="opacity:0;"。非锚点场景有 style="visibility:hidden;"
  • 每个非锚点场景都有包含 autoAlpha 的 tl.set(不是 visibility)。开始时 autoAlpha: 1,结束时 autoAlpha: 0。
  • 每个着色器组中的首个锚点场景都有 tl.set("#sN", { opacity: 1 }, startTime)。否则该场景会保持不可见。
  • 场景时间窗口首尾相接(没有间隙)
  • 着色器转场边界位于时间窗口内:time < boundary < time + duration
  • 转场时长不短于 0.3s
  • 除最后一个场景外,没有退出补间
  • 没有 Date.now()、未使用种子的 Math.random()、repeat: -1
  • 没有将 SVG 滤镜数据 URL 用作 background-image
  • window.__timelines["main"] = tl 与 data-composition-id 相匹配

品牌与内容准确性(你的核心工作——务必做到):

  • 颜色与简报 / 附件完全一致
  • 不使用禁用字体
  • 最小字号:标题 60px 以上,正文 20px 以上,标签 16px 以上
  • 数字列使用 font-variant-numeric: tabular-nums
  • 每个场景都有有意义的内容(不是占位文本)
  • 场景数量和时长与视频类型相匹配

动画基线(足以开始——Claude Code 会进一步润色):

  • 每个场景至少有一个入场补间(tl.from)
  • 每个超过 4s 的场景至少有一处场景中活动(浮动、计数、发光)
  • 没有完全静止的场景(完全没有补间)
  • 场景文字在限定时间内清晰可读

第 7 节:骨架代码

preview.html(通用——所有视频类型均原样复制)
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>HyperFrames Preview</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        background: #111;
        height: 100%;
        overflow: hidden;
      }
    </style>
    <script type="module" src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>
  </head>
  <body>
    <hyperframes-player
      id="p"
      controls
      autoplay
      muted
      style="display:block;width:100vw;height:100vh"
    ></hyperframes-player>
    <script>
      document.getElementById("p").setAttribute("src", "./index.html" + location.search);
    </script>
  </body>
</html>
README.md(通用——替换 <project-name>)
# <project-name>

A HyperFrames video composition. Plain HTML + GSAP; rendered to MP4 by the `hyperframes` CLI.

## Requirements

- **Node.js 22+** -- [nodejs.org](https://nodejs.org/)
- **FFmpeg** -- `brew install ffmpeg` (macOS) or `sudo apt install ffmpeg` (Debian/Ubuntu) or [ffmpeg.org/download](https://ffmpeg.org/download.html) (Windows)

Verify: `npx hyperframes doctor`

## Preview

```bash
npx hyperframes preview
```

Opens the HyperFrames Studio at `http://localhost:3002` with frame-accurate scrubbing.

## Refine with Claude Code

This project was drafted in Claude Design. To polish animations, timing, and pacing:

```bash
npx skills add heygen-com/hyperframes   # install HyperFrames skills (one-time)
npx hyperframes lint                     # verify structure (should pass with zero errors)
npx hyperframes preview                  # open the studio for live feedback
```

Then open in Claude Code and iterate:

- "Make scene 3's entrance snappier"
- "Add a counter animation to the stat in scene 5"
- "Tighten the pacing -- scenes 4 and 6 feel too long"
- "Change the shader on transition 3 to glitch"

## Render

```bash
npx hyperframes render index.html -o output.mp4
```

1920x1080 / 30fps by default. Use `--fps 60` or `--resolution 3840x2160` to override.
骨架 A——社交短视频(1080x1920,15s,6 个场景)

转场计划:s1→s2 硬切,s2→s3 硬切,s3→s4 着色器(主角揭晓),s4→s5 硬切,s5→s6 硬切。中点使用一个着色器。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=1080, height=1920" />
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@hyperframes/shader-transitions/dist/index.global.js"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    
    <link
      href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;500;700&family=JetBrains+Mono:wght@400&display=swap"
      rel="stylesheet"
    />
    <style>
      :root {
        /* === FILL: Your brand identity === */
        --bg: #0a0a0d;
        --ink: #f5f5f7;
        --accent: #7c6cff;
        --muted: #5a6270;
        --accent-dim: #3d3680;
        --font-display: "Space Grotesk", sans-serif;
        --font-data: "JetBrains Mono", monospace;
      }

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      html,
      body {
        width: 1080px;
        height: 1920px;
        overflow: hidden;
        background: var(--bg);
        color: var(--ink);
      }

      .scene {
        position: absolute;
        top: 0;
        left: 0;
        width: 1080px;
        height: 1920px;
        overflow: hidden;
      }
      .scene-content {
        width: 100%;
        height: 100%;
        padding: 120px 80px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 24px;
        box-sizing: border-box;
        position: relative;
        z-index: 1;
      }
      .clip {
      }

      .display {
        font-family: var(--font-display);
        font-weight: 700;
        line-height: 1.1;
      }
      .body-text {
        font-family: var(--font-display);
        font-weight: 300;
        line-height: 1.4;
        color: var(--muted);
      }
      .data-text {
        font-family: var(--font-data);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
      }

      .grain {
        position: absolute;
        inset: 0;
        pointer-events: none;
        z-index: 50;
        opacity: 0.18;
        background-image:
          radial-gradient(rgba(255, 255, 255, 0.08) 1px, transparent 1.2px),
          radial-gradient(rgba(0, 0, 0, 0.18) 1px, transparent 1.2px);
        background-size:
          3px 3px,
          5px 5px;
        background-position:
          0 0,
          1px 2px;
        mix-blend-mode: overlay;
      }

      /* === FILL: Per-scene styles below === */
    </style>
  </head>
  <body>
    <div
      id="main"
      data-composition-id="main"
      data-width="1080"
      data-height="1920"
      data-start="0"
      data-duration="15"
    >
      
      <div class="scene clip" id="s1" data-start="0" data-duration="2.5" data-track-index="0">
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>

      <div
        class="scene clip"
        id="s2"
        data-start="2.5"
        data-duration="2.5"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>

      
      <div
        class="scene clip"
        id="s3"
        data-start="5"
        data-duration="2.5"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>

      
      <div
        class="scene clip"
        id="s4"
        data-start="7.5"
        data-duration="2.5"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>

      <div
        class="scene clip"
        id="s5"
        data-start="10"
        data-duration="2.5"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>

      <div
        class="scene clip"
        id="s6"
        data-start="12.5"
        data-duration="2.5"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="scene-content">
          
        </div>
      </div>
    </div>

    <script>
      window.__timelines = window.__timelines || {};
      var tl = gsap.timeline({ paused: true });

      // --- Non-anchor scene toggles (REQUIRED — use autoAlpha) ---
      tl.set("#s1", { autoAlpha: 0 }, 2.5);
      tl.set("#s2", { autoAlpha: 1 }, 2.5);
      tl.set("#s2", { autoAlpha: 0 }, 5.0);
      // s3, s4 are shader anchors — HyperShader manages their opacity
      tl.set("#s3", { opacity: 1 }, 5.0); // first anchor must be explicitly shown
      tl.set("#s5", { autoAlpha: 1 }, 10.0);
      tl.set("#s5", { autoAlpha: 0 }, 12.5);
      tl.set("#s6", { autoAlpha: 1 }, 12.5);

      // === SCENE 1 (0-2.5s) — hook ===
      // FILL: entrance + mid-scene activity (use 2+ patterns from Section 8)

      // === SCENE 2 (2.5-5s) ===
      // FILL: entrance + mid-scene activity

      // === SCENE 3 (5-7.5s) — SHADER ANCHOR, no exit tweens ===
      // FILL: entrance + mid-scene activity

      // === SCENE 4 (7.5-10s) — hero (shader reveals this) ===
      // FILL: entrance + mid-scene activity

      // === SCENE 5 (10-12.5s) — proof ===
      // FILL: entrance + mid-scene activity

      // === SCENE 6 (12.5-15s) — CTA, final scene, exit OK ===
      // FILL: entrance + mid-scene activity + optional exit

      // --- Shader: 1 transition at the hero reveal ---
      window.HyperShader.init({
        bgColor:
          getComputedStyle(document.documentElement).getPropertyValue("--bg").trim() || "#0a0a0d",
        scenes: ["s3", "s4"],
        timeline: tl,
        transitions: [{ time: 7.25, shader: "cinematic-zoom", duration: 0.5 }],
      });

      window.__timelines["main"] = tl;
    </script>
  </body>
</html>
骨架 B——发布预告片(1920x1080,25s,8 个场景)

转场计划:s1→s2 硬切,s2→s3 硬切,s3→s4 硬切,s4→s5 着色器(主角揭晓),s5→s7 着色器(能量转换,s6 作为运行时管理的插播片段播放),s7→s8 着色器(CTA 落版)。7 次切换中有 3 次使用着色器。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=1920, height=1080" />
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@hyperframes/shader-transitions/dist/index.global.js"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    
    <link
      href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;500;700&family=JetBrains+Mono:wght@400&display=swap"
      rel="stylesheet"
    />
    <style>
      :root {
        /* === FILL: Your brand identity === */
        --bg: #0a0a0d;
        --ink: #f5f5f7;
        --accent: #7c6cff;
        --muted: #5a6270;
        --accent-dim: #3d3680;
        --font-display: "Space Grotesk", sans-serif;
        --font-data: "JetBrains Mono", monospace;
      }

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      html,
      body {
        width: 1920px;
        height: 1080px;
        overflow: hidden;
        background: var(--bg);
        color: var(--ink);
      }

      .scene {
        position: absolute;
        top: 0;
        left: 0;
        width: 1920px;
        height: 1080px;
        overflow: hidden;
      }
      .scene-content {
        width: 100%;
        height: 100%;
        padding: 100px 160px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 24px;
        box-sizing: border-box;
        position: relative;
        z-index: 1;
      }
      .clip {
      }

      .display {
        font-family: var(--font-display);
        font-weight: 700;
        line-height: 1.1;
      }
      .body-text {
        font-family: var(--font-display);
        font-weight: 300;
        line-height: 1.4;
        color: var(--muted);
      }
      .data-text {
        font-family: var(--font-data);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
      }

      .grain {
        position: absolute;
        inset: 0;
        pointer-events: none;
        z-index: 50;
        opacity: 0.18;
        background-image:
          radial-gradient(rgba(255, 255, 255, 0.08) 1px, transparent 1.2px),
          radial-gradient(rgba(0, 0, 0, 0.18) 1px, transparent 1.2px);
        background-size:
          3px 3px,
          5px 5px;
        background-position:
          0 0,
          1px 2px;
        mix-blend-mode: overlay;
      }

      .vignette {
        position: absolute;
        inset: 0;
        pointer-events: none;
        z-index: 49;
        background: radial-gradient(ellipse at center, transparent 50%, rgba(0, 0, 0, 0.4) 100%);
      }

      /* === FILL: Per-scene styles below === */
    </style>
  </head>
  <body>
    <div
      id="main"
      data-composition-id="main"
      data-width="1920"
      data-height="1080"
      data-start="0"
      data-duration="25"
    >
      
      <div class="scene clip" id="s1" data-start="0" data-duration="3" data-track-index="0">
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      
      <div
        class="scene clip"
        id="s2"
        data-start="3"
        data-duration="3"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      <div
        class="scene clip"
        id="s3"
        data-start="6"
        data-duration="3"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      
      <div
        class="scene clip"
        id="s4"
        data-start="9"
        data-duration="3.5"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      <div
        class="scene clip"
        id="s5"
        data-start="12.5"
        data-duration="3"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      
      <div
        class="scene clip"
        id="s6"
        data-start="15.5"
        data-duration="3"
        data-track-index="0"
        style="visibility:hidden;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      
      <div
        class="scene clip"
        id="s7"
        data-start="18.5"
        data-duration="3"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>

      <div
        class="scene clip"
        id="s8"
        data-start="21.5"
        data-duration="3.5"
        data-track-index="0"
        style="opacity:0;"
      >
        <div class="grain"></div>
        <div class="vignette"></div>
        <div class="scene-content"></div>
      </div>
    </div>

    <script>
      window.__timelines = window.__timelines || {};
      var tl = gsap.timeline({ paused: true });

      // --- Non-anchor scene visibility toggles (REQUIRED) ---
      tl.set("#s1", { autoAlpha: 0 }, 3.0);
      tl.set("#s2", { autoAlpha: 1 }, 3.0);
      tl.set("#s2", { autoAlpha: 0 }, 6.0);
      tl.set("#s3", { autoAlpha: 1 }, 6.0);
      tl.set("#s3", { autoAlpha: 0 }, 9.0);

      // --- First shader anchor must be explicitly shown ---
      tl.set("#s4", { opacity: 1 }, 9.0);

      // s4, s5 are shader anchors — HyperShader manages their opacity after transitions
      tl.set("#s6", { autoAlpha: 1 }, 15.5);
      tl.set("#s6", { autoAlpha: 0 }, 18.5);

      // --- Second shader group's first anchor must also be shown ---
      tl.set("#s7", { opacity: 1 }, 18.5);
      // s7, s8 are shader anchors — HyperShader manages their opacity after transitions

      // === SCENE 1 (0-3s) — hook ===

      // === SCENE 2 (3-6s) — hard cut ===

      // === SCENE 3 (6-9s) — hard cut ===

      // === SCENE 4 (9-12.5s) — SHADER ANCHOR, no exit tweens ===

      // === SCENE 5 (12.5-15.5s) — shader from s4, hero reveal ===

      // === SCENE 6 (15.5-18.5s) — hard cut ===

      // === SCENE 7 (18.5-21.5s) — SHADER ANCHOR, no exit tweens ===

      // === SCENE 8 (21.5-25s) — shader from s7, CTA. Final, exit OK ===

      // --- Shader transitions: 2 key moments ---
      // s4->s5 (hero reveal) and s7->s8 (CTA landing)
      // HyperShader requires consecutive anchors, so we use two groups:
      // Group 1: [s4, s5] with 1 transition
      // Group 2: [s7, s8] with 1 transition
      // But HyperShader only supports one init() call, so we chain them:
      // [s4, s5] — shader — then runtime hard-cuts s5->s6->s7 — then [s7, s8]
      // To satisfy the invariant with one init(), we include s5->s7 gap scenes.
      // Simplest: just use one contiguous anchor block [s4, s5, s7, s8] with
      // the s5->s7 transition as a real (visible) shader too. This gives you
      // 3 shaders total — still well under the "every cut" anti-pattern.
      window.HyperShader.init({
        bgColor:
          getComputedStyle(document.documentElement).getPropertyValue("--bg").trim() || "#0a0a0d",
        scenes: ["s4", "s5", "s7", "s8"],
        timeline: tl,
        transitions: [
          { time: 12.25, shader: "cinematic-zoom", duration: 0.5 },
          { time: 15.25, shader: "light-leak", duration: 0.5 },
          { time: 21.25, shader: "cross-warp-morph", duration: 0.5 },
        ],
      });

      window.__timelines["main"] = tl;
    </script>
  </body>
</html>
骨架 C——产品解说视频(1920x1080,45s,12 个场景)

沿用骨架 B 的结构,但使用 12 个场景 div(s1-s12),data-duration 总计为 45s,并包含 11 次转场。根据内容密度调整场景时长:混合使用 3s、3.5s、4s 和 5s 的场景。场景节奏示例:3-3-4-3.5-4-5-3.5-4-3.5-4-4-3.5。

骨架 D——电影感标题(1920x1080,60s,7 个场景)

沿用相同结构,使用 7 个场景 div(s1-s7),延长场景时长(每个 6-10s),减少转场(6 次),并更克制地使用着色器(cross-warp-morph、thermal-distortion)。场景节奏:8-7-8-10-9-10-8。


第 8 节:常见动画模式

复制粘贴以下代码。这些模式会出现在每个生产级合成中。

计数器动画
var counterObj = { v: 0 };
tl.to(
  counterObj,
  {
    v: 1900000000000,
    duration: 2.0,
    ease: "power2.out",
    onUpdate: function () {
      document.getElementById("s3-stat").textContent = "$" + (counterObj.v / 1e12).toFixed(1) + "T";
    },
  },
  10.5,
);
SVG 描边绘制
<svg viewBox="0 0 400 200" style="position:absolute; bottom:100px; left:160px;">
  <path
    id="s2-line"
    d="M 0 100 Q 200 20 400 100"
    stroke="var(--accent)"
    stroke-width="3"
    fill="none"
    stroke-linecap="round"
    stroke-dasharray="440"
    stroke-dashoffset="440"
  />
</svg>
tl.to("#s2-line", { strokeDashoffset: 0, duration: 1.0, ease: "power2.out" }, 3.5);
字符错开动画
<h1 class="display" style="font-size:120px;">
  <span class="char">N</span><span class="char">O</span><span class="char">R</span>
  <span class="char">T</span><span class="char">H</span>
</h1>
tl.from(
  ".char",
  {
    y: 60,
    autoAlpha: 0,
    duration: 0.5,
    ease: "power3.out",
    stagger: { each: 0.12, from: "start" },
  },
  29.5,
);
呼吸式浮动(场景中的动态效果)
tl.to(
  "#s4-logo",
  {
    y: -5,
    duration: 1.5,
    ease: "sine.inOut",
    yoyo: true,
    repeat: 1,
  },
  15.0,
);
条形图填充
["#bar1", "#bar2", "#bar3", "#bar4"].forEach(function (sel, i) {
  tl.from(
    sel,
    {
      scaleY: 0,
      transformOrigin: "bottom",
      duration: 0.6,
      ease: "expo.out",
    },
    11.0 + i * 0.15,
  );
});
环绕 / 旋转
tl.to(
  "#orbit-dot",
  {
    rotation: 360,
    duration: 3.0,
    ease: "none",
    transformOrigin: "50% 200px",
  },
  8.5,
);
高光扫过(background-size 动画)
#s5-headline {
  background: linear-gradient(var(--accent), var(--accent)) no-repeat 0 85% / 0% 30%;
}
tl.to("#s5-headline", { backgroundSize: "100% 30%", duration: 0.6, ease: "power2.out" }, 22.0);
CSS radial-gradient 颗粒效果(兼容 Safari + Claude Design iframe)
.grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 50;
  opacity: 0.18;
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.08) 1px, transparent 1.2px),
    radial-gradient(rgba(0, 0, 0, 0.18) 1px, transparent 1.2px);
  background-size:
    3px 3px,
    5px 5px;
  background-position:
    0 0,
    1px 2px;
  mix-blend-mode: overlay;
}

绝不要使用 SVG filter data:image/svg+xml 颗粒效果——它会污染 Safari 中的 html2canvas,导致 Claude Design 跨域 iframe 中的所有着色器转场失效。


参考资料(仅在需要时获取)

以上已内联所有关键信息。以下资料适用于特殊情况:

评论

支持 Markdown