OpenMontage HyperFrames HTML-in-Canvas 实战指南:把任意 HTML/CSS 实时捕获为 GPU 纹理并叠加 WebGL 视觉特效
本篇指南围绕 OpenMontage 仓库中 HyperFrames 动画技能集的核心适配器 html-in-canvas-patterns.md 展开,系统讲解「把实时 DOM 渲染为 GPU 纹理,再交由 Three.js / WebGL2 / GLSL 后期处理呈现」的完整套路。读完你将掌握:一份可复用的双 Canvas 样板代码、5 类开箱效果与 5 段可直接改用的 GLSL 后处理片段、任意自定义 Shader 效果的四步接入方法,以及「何时用 HTML-in-Canvas、何时退回标准 GSAP」的取舍依据。
背景:HTML-in-Canvas 在 HyperFrames 中的定位
HyperFrames 是一种「从 HTML 渲染视频」的确定性动画运行时:一部作品本质是一个 HTML 文件,DOM 用 data-* 属性声明时间,动画由一条 paused GSAP 时间线驱动,渲染引擎按时间线逐帧 seek、截图、编码成 MP4(hyperframes-core 组合契约)。在 OpenMontage 仓库中,HyperFrames 被封装为一套 .agents/skills/hyperframes-* 分层技能(核心入口、动画技能总览),并作为与 Remotion 并列的 render_runtime 选项接入各生产管线(见 OpenMontage 侧的 HyperFrames 桥接指南)。
在整套动画技能里,adapters/html-in-canvas-patterns.md 被定位为 HyperFrames 最强的一种视觉能力:它借助 Chrome 的 layoutSubtree + drawElementImage 实验 API,把页面上任何一段「活的」HTML/CSS(文字、图片、卡片、仪表盘……)直接光栅化进 canvas 纹理,随后无论塞进 WebGL 片元着色器、贴上 Three.js 3D 平面、还是走 EffectComposer 后期链,都是对同一份「像素级精确的实时 UI」做艺术化处理。
但技能文档明确强调一条纪律:一段视频只该有 1~3 个 hero beat 使用它,其余 beat 继续用普通 GSAP。原因不只是性能,更在于叙事——「平面 GSAP beat」与「HTML-in-Canvas 电影级 beat」之间的落差本身,就是视觉叙事的一部分。这与 hyperframes-animation/techniques.md 中「capturing live HTML/CSS as a GPU texture 是独立的重型能力,只用于 1–3 个 hero beat」的指引一致,也与该技能默认的「先选 2–4 条原子规则拼时间线」工作流形成互补(SKILL.md)。
核心样板:每种 HTML-in-Canvas 效果的共同骨架
无论最终呈现是 3D 旋转、液体表面还是门户传送,结构都只有一套。先学会它,任何效果都能基于它改写。
第一步:源画布与输出画布(HTML)
源画布 <canvas id="hic-source"> 带有 layoutsubtree 属性,里面套一个确定尺寸的内容容器 <div id="hic-content">(1920×1080),你的全部 HTML 内容写在这里;整个源画布绝对定位并设 opacity: 0,它只负责「被截图」,不负责「被看见」。真正可见的是第二块输出画布 hic-output:
<!-- 1. Source HTML — your content goes inside a layoutsubtree canvas -->
<canvas
id="hic-source"
layoutsubtree
width="1920"
height="1080"
style="position:absolute;inset:0;opacity:0;"
>
<div id="hic-content" style="width:1920px;height:1080px;">
<!-- YOUR HTML CONTENT HERE — text, images, cards, dashboards, anything -->
</div>
</canvas>
<!-- 2. Render target — the visible canvas that shows the effect -->
<canvas id="hic-output" width="1920" height="1080" style="position:absolute;inset:0;"></canvas>
第二步:特性检测——永远检查、永远兜底(JS)
drawElementImage 是实验能力,未启用 Chrome flag 的浏览器里根本不存在。因此每一份组合都要先探测:先检查 canvas 是否支持 layoutSubtree 属性,再取 2D 上下文并确认 drawElementImage 是函数。
// 3. Feature detection — always check, always provide fallback
function isHiCSupported() {
var tc = document.createElement("canvas");
if (!("layoutSubtree" in tc)) return false;
tc.setAttribute("layoutsubtree", "");
var ctx = tc.getContext("2d");
return ctx && typeof ctx.drawElementImage === "function";
}
var apiOk = isHiCSupported();
第三步:逐帧捕获函数(JS)
捕获逻辑只有一行——把 hic-content 用 drawElementImage 画进源画布的 2D 上下文。它必须在动画的每一帧都被调用,因为你要捕获的是「当前时刻的 DOM 状态」。
// 4. Capture function — call this every frame in onUpdate
var capCanvas = document.getElementById("hic-source");
var capCtx = capCanvas.getContext("2d");
function captureContent() {
if (apiOk) {
capCtx.drawElementImage(document.getElementById("hic-content"), 0, 0, 1920, 1080);
}
}
第四步:用 GSAP 时间线驱动——每帧「先捕获、再渲染」(JS)
HyperFrames 的一切动画都收敛在一条 paused GSAP 时间线(文档中的 tl)上。做法是 tweens 一个普通对象代理 proxy,在 onUpdate 回调里同步执行「捕获内容 + 渲染你的效果」:
// 5. Drive from GSAP timeline — capture + render every frame
tl.to(
proxy,
{
/* your animation properties */
duration: BEAT_DURATION,
ease: "sine.inOut",
onUpdate: function () {
captureContent();
// render your effect here (Three.js or WebGL2)
},
},
0,
);
值得注意的是:onUpdate 回调与 tl.call() 在这套确定性运行时里是被官方允许、甚至被依赖的——canvas/WebGL 渲染与逐字打字模式都靠它们驱动(第 484 行附近关于 linter 的说明)。真正被禁止的是 Math.random、Date.now、requestAnimationFrame、repeat: -1 这类会破坏「同一时间线每一帧画面可复现」的行为。
兜底策略:当 drawElementImage 不可用(例如未加 Chrome flag 的普通预览环境)时,用纯色占位或 Canvas 2D 文字代替内容。HyperFrames 渲染引擎在正式出片时会自动加上对应 flag,因此最终视频里效果一定生效——预览环境的兜底只影响本地调试观感,不影响产物。完整的兜底实现可以参考 liquid-glass 相关 block(实现细节见下文「效果目录」第 4/5 节的 registry 指引)。
与 WebGPU 类效果的环境差异:同属本仓库的 TypeGPU/WebGPU 适配器文档 typegpu.md 进一步说明,渲染引擎向 Chrome 启动参数自动追加
--enable-unsafe-webgpu与--enable-features=CanvasDrawElement;但原版 Chromium 与随附的 headless-shell 无法同时支持 WebGPU +drawElementImage。需要该组合(如ios26-liquid-glass、vfx-liquid-glass等 block)时,应先把PRODUCER_HEADLESS_SHELL_PATH指向 Brave 或 Chrome canary 的可执行文件,再执行npx hyperframes render/preview。纯 TypeGPU 图层(不涉及 HTML 纹理)则可以直接在 headless-shell 里工作。
效果目录 1:3D 旋转 + Bloom 辉光(Three.js)
观感:内容悬浮在 3D 空间中缓慢旋转,亮边带着电影级辉光——像一件产品截图陈列在黑暗放映厅里。
适用场景:Hero 产品展示、功能揭晓、需要高级感的 CTA。
关键 Three.js 组件:PlaneGeometry + CanvasTexture + EffectComposer + UnrealBloomPass。
在样板代码之后追加:
// After the boilerplate above, add:
var scene3d = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, 1920 / 1080, 0.1, 100);
camera.position.set(0, 0, 4);
var renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("hic-output"),
antialias: true,
alpha: true,
});
renderer.setSize(1920, 1080);
var texture = new THREE.CanvasTexture(capCanvas);
var mesh = new THREE.Mesh(
new THREE.PlaneGeometry(3.6, 2.2),
new THREE.MeshBasicMaterial({ map: texture }),
);
scene3d.add(mesh);
// Post-processing: bloom for cinematic glow.
// EffectComposer / RenderPass / UnrealBloomPass are ES-module named imports
// (see the import block below) — they're NOT properties of THREE in modern
// versions. Three.js r150+ removed the UMD `examples/js/` globals.
var composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene3d, camera));
composer.addPass(new UnrealBloomPass(new THREE.Vector2(1920, 1080), 0.3, 0.4, 0.85));
var proxy = { rotY: -0.12, zoom: 4.2 };
tl.to(
proxy,
{
rotY: 0.12,
zoom: 3.6,
duration: BEAT_DURATION,
ease: "sine.inOut",
onUpdate: function () {
captureContent();
texture.needsUpdate = true;
mesh.rotation.y = proxy.rotY;
camera.position.z = proxy.zoom;
composer.render();
},
},
0,
);
两个关键细节:
- 捕获画布被包成
THREE.CanvasTexture,texture.needsUpdate = true必须放在每帧更新里,否则 GPU 侧的纹理不会随捕获内容刷新; - 相机拉近/旋转由 GSAP 代理
rotY、zoom驱动,动画曲线集中在时间线一处管理。
用 ESM 加载 Three.js 与后期处理(需要 type="module" 脚本):
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.181.2/+esm";
import { EffectComposer } from "https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from "https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/ShaderPass.js";
import { UnrealBloomPass } from "https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/UnrealBloomPass.js";
// ... rest of composition code using these imports
</script>
版本选择要特别留意:Three.js r150+ 已移除 UMD 形态的 examples/js/ 全局变量,examples/js/ 路径更是在 r152 被彻底删除。所以请坚持用 ESM 的 examples/jsm/ 路径搭配 three@0.181.2——这也是 HyperFrames Three.js 适配器锁定的版本(仓库内 three 适配器 与 能力清单 均记录同一加载地址)。
效果目录 2:磁性光标扭曲(原生 WebGL2)
观感:内容朝一个移动中的点弯曲、被「磁吸」,畸变处出现 RGB 通道分离的色差。 适用场景:需要交互感的产品演示、光标跟随的「请看这个功能」时刻。 关键技术:纯 WebGL2 的自定义片元着色器,实现高斯形 warp + 色差分裂,不需要 Three.js。
// WebGL2 setup
var gl = document.getElementById("hic-output").getContext("webgl2", {
alpha: false,
preserveDrawingBuffer: true,
});
// Vertex shader — full-screen quad
var VS = `#version 300 es
in vec2 a_pos;
out vec2 v_uv;
void main() {
v_uv = a_pos * 0.5 + 0.5;
gl_Position = vec4(a_pos, 0.0, 1.0);
}`;
// Fragment shader — magnetic warp + chromatic aberration
var FS = `#version 300 es
precision highp float;
in vec2 v_uv;
out vec4 fragColor;
uniform sampler2D u_tex;
uniform vec2 u_cursor; // cursor position (0-1)
uniform float u_strength; // warp strength (0-1)
void main() {
vec2 uv = v_uv;
vec2 delta = uv - u_cursor;
float dist = length(delta);
float warp = u_strength * exp(-dist * dist * 8.0);
vec2 warped = uv - delta * warp * 0.3;
// Chromatic aberration at distortion site
float aberration = warp * 0.008;
float r = texture(u_tex, warped + vec2(aberration, 0.0)).r;
float g = texture(u_tex, warped).g;
float b = texture(u_tex, warped - vec2(aberration, 0.0)).b;
fragColor = vec4(r, g, b, 1.0);
}`;
// Compile, link, setup quad geometry, upload texture...
// (See registry/blocks/vfx-magnetic/vfx-magnetic.html for complete implementation)
// Drive cursor position from GSAP
var proxy = { cx: 0.2, cy: 0.5, strength: 0.0 };
tl.to(
proxy,
{
cx: 0.8,
cy: 0.4,
strength: 1.0,
duration: BEAT_DURATION,
ease: "power2.inOut",
onUpdate: function () {
captureContent();
// Upload texture, set uniforms, draw
gl.uniform2f(cursorLoc, proxy.cx, proxy.cy);
gl.uniform1f(strengthLoc, proxy.strength);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
},
},
0,
);
这套代码展示了 HTML-in-Canvas 的另一种形态:着色器不是包在 3D 场景里,而是直接消费纹理。其中顶点着色器输出一个覆盖全屏的四边形,v_uv = a_pos * 0.5 + 0.5 把裁剪坐标 [-1,1] 换算为纹理坐标 [0,1];gl.getContext("webgl2", { preserveDrawingBuffer: true }) 保证逐帧捕获时绘制缓冲不丢帧。
效果目录 3:粉碎 / 碎片爆炸(Three.js)
观感:内容碎成几何碎片向四周飞散,露出后方内容。
适用场景:戏剧性转场、「挣脱束缚」时刻、张力释放。
关键技术:用 BufferGeometry 把源纹理细分出三角形网格碎片,再用 GSAP 逐片驱动位置与旋转。
完整 1156 行实现请研究注册表里的
registry/blocks/vfx-shatter/vfx-shatter.html(本仓库中vfx-shatter作为可安装的 VFX registry block 存在,见 registry 发现文档 中 "VFX (6)" 分类表)。核心思路如下:
// 1. Capture content to texture (same boilerplate)
// Seeded PRNG for determinism — Math.random() is banned
function mulberry32(seed) {
return function () {
seed |= 0;
seed = (seed + 0x6d2b79f5) | 0;
var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
var rng = mulberry32(42);
// 2. Create N triangle fragments from the texture
var fragments = [];
for (var i = 0; i < NUM_FRAGMENTS; i++) {
var geom = new THREE.BufferGeometry();
var mesh = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({ map: texture }));
scene3d.add(mesh);
fragments.push({ mesh: mesh, targetPos: randomExplosionVector(rng), delay: rng() * 0.5 });
}
// 3. Animate: first hold still, then EXPLODE
tl.to({}, { duration: holdTime }, 0);
fragments.forEach(function (frag) {
tl.to(
frag.mesh.position,
{
x: frag.targetPos.x,
y: frag.targetPos.y,
z: frag.targetPos.z,
duration: 0.8,
ease: "power3.in",
},
holdTime + frag.delay,
);
tl.to(
frag.mesh.rotation,
{ x: rng() * 4, y: rng() * 4, duration: 0.8, ease: "power2.in" },
holdTime + frag.delay,
);
});
确定性是这个效果的重中之重:Math.random() 在 HyperFrames 中是被禁用的(linter 会直接拦截),因此这里引入 mulberry32(42) 种子化伪随机数生成器,并用固定种子产生爆炸向量与延迟——同一时间线、同一帧,画面永远可复现,这正是 技能硬性约束 中「seeded PRNG、禁 Math.random」规则在视觉特效里的直接应用。时间编排上先让碎片 holdTime 原地静止,再按各自 delay 逐个以 power3.in 加速炸出。
效果目录 4:液体 / 流体表面(Three.js)
观感:内容浮在一片带实时波动动力学的涟漪液面上;也可以让内容本身就是液面,如水般起伏。
适用场景:有机感/高级感、氛围背景、「活的」产品展示。
关键技术:细分 PlaneGeometry + 顶点着色器里的噪声函数做顶点位移。
完整 1244 行实现参考
registry/blocks/vfx-liquid-background/vfx-liquid-background.html(同样作为 registry block 存在)。核心思路:
// Custom vertex shader with wave displacement
var vertexShader = `
varying vec2 vUv;
uniform float u_time;
void main() {
vUv = uv;
vec3 pos = position;
// Sine wave displacement
pos.z += sin(pos.x * 3.0 + u_time * 2.0) * 0.15;
pos.z += cos(pos.y * 2.5 + u_time * 1.5) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
var mesh = new THREE.Mesh(
new THREE.PlaneGeometry(4, 3, 64, 64), // heavily subdivided for smooth waves
new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: `varying vec2 vUv; uniform sampler2D u_tex;
void main() { gl_FragColor = texture2D(u_tex, vUv); }`,
uniforms: {
u_tex: { value: texture },
u_time: { value: 0 },
},
}),
);
要点解析:
PlaneGeometry(4, 3, 64, 64)的 64×64 高度细分是为了给波提供足够网格顶点,细分不足会出现多边形化折痕;- 波形由两路不同频率/相位的三角波叠加(
sin(x*3.0 + t*2.0)与cos(y*2.5 + t*1.5)),各自独立振幅(0.15 与 0.1),叠加后更接近自然的复合波; u_time这个 uniform 需由外层 GSAPonUpdate驱动(texture与u_time都在uniforms中声明,别忘把捕获纹理作为u_tex传入)。
效果目录 5:传送门 / 维度揭示(Three.js)
观感:一个发光的圆形传送门缓缓打开,内容从另一个维度穿门而出。
适用场景:产品揭晓、「进入应用内部」时刻、Hero 功能导入。
实现方式:完整 863 行实现参考 registry/blocks/vfx-portal/vfx-portal.html(registry block vfx-portal,见 registry 发现文档)。它同样建立在核心样板之上——捕获 HTML 内容、采样为纹理、套用圆形 SDF 掩膜与辉光着色器。
更多可自建效果:五个单函数 GLSL 后处理模板
以下效果不在官方 VFX block 里,需要你自己基于核心样板 + 一段自定义片元着色器构建。每个效果本质上都是「应用到已捕获纹理上的一个 GLSL 函数」。
效果 6:噪点溶解(Noise Dissolve)
内容溶解为噪点颗粒、露出后方。适合转场。u_progress 0.0 = 完全可见,1.0 = 完全溶解,溶解边界带橙色辉光边缘:
// Fragment shader — noise-based dissolve
uniform float u_progress; // 0.0 = fully visible, 1.0 = fully dissolved
uniform sampler2D u_tex;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
void main() {
vec2 uv = v_uv;
float noise = hash(uv * 50.0);
float threshold = u_progress;
if (noise < threshold) {
// Edge glow at the dissolve boundary
float edge = smoothstep(threshold - 0.05, threshold, noise);
fragColor = vec4(1.0, 0.6, 0.2, 1.0) * (1.0 - edge); // orange edge glow
} else {
fragColor = texture(u_tex, uv);
}
}
效果 7:全息 / 虹彩(Holographic / Iridescent)
内容覆盖一层随时间漂移的彩虹式全息光晕,未来感强。核心是用位置+时间的角度合成三路相位差 120° 的正弦值形成 RGB:
uniform float u_time;
uniform sampler2D u_tex;
void main() {
vec4 color = texture(u_tex, v_uv);
// Iridescent color shift based on position + time
float angle = v_uv.x * 6.28 + v_uv.y * 3.14 + u_time * 0.5;
vec3 holo = vec3(
sin(angle) * 0.5 + 0.5,
sin(angle + 2.094) * 0.5 + 0.5,
sin(angle + 4.189) * 0.5 + 0.5
);
// Blend holographic over content (subtle overlay)
fragColor = vec4(mix(color.rgb, holo, 0.15 + 0.1 * sin(u_time)), color.a);
}
注意 2.094 ≈ 2π/3、4.189 ≈ 4π/3,三通道相位均匀错开才产生「白平衡的」彩色循环;混叠系数 0.15 + 0.1*sin(u_time) 让全息强度随脉动呼吸,保持克制才不会抢走内容可读性。
效果 8:扫描线 + CRT(Scan Lines + CRT)
复古显示器观感:扫描线、轻微桶形曲率、荧光粉辉光。适合「代码」「终端」类 beat:
uniform sampler2D u_tex;
uniform float u_time;
void main() {
vec2 uv = v_uv;
// Barrel distortion (CRT curvature)
vec2 centered = uv - 0.5;
float dist = dot(centered, centered);
uv = uv + centered * dist * 0.15;
vec4 color = texture(u_tex, uv);
// Scan lines
float scanline = sin(uv.y * 800.0) * 0.04;
color.rgb -= scanline;
// Slight RGB offset (phosphor)
color.r = texture(u_tex, uv + vec2(0.001, 0.0)).r;
color.b = texture(u_tex, uv - vec2(0.001, 0.0)).b;
// Vignette
float vignette = 1.0 - dist * 2.0;
fragColor = vec4(color.rgb * vignette, 1.0);
}
三段处理各司其职:桶形畸变把边缘向外推(uv + centered * dist * 0.15,dist 是到中心的平方距离);扫描线用高频 sin(uv.y * 800) 做暗纹减光;RGB 通道 ±0.001 的水平偏移模拟荧光粉成像偏移;最后平方距离做暗角。
效果 9:磨砂玻璃模糊(Frosted Glass Blur)
被磨砂玻璃挡住的背后内容——可见但被柔化,带轻微光折射。适合「幕后」「敬请期待」时刻。u_blur 0.0 = 清晰,1.0 = 全磨砂:
uniform sampler2D u_tex;
uniform float u_blur; // 0.0 = clear, 1.0 = full frost
void main() {
vec2 uv = v_uv;
vec4 color = vec4(0.0);
// Box blur with offset
float radius = u_blur * 0.015;
for (float x = -2.0; x <= 2.0; x += 1.0) {
for (float y = -2.0; y <= 2.0; y += 1.0) {
color += texture(u_tex, uv + vec2(x, y) * radius);
}
}
color /= 25.0;
// Add frost noise texture
float frost = fract(sin(dot(uv * 200.0, vec2(12.9898, 78.233))) * 43758.5453);
color.rgb += frost * 0.03 * u_blur;
fragColor = color;
}
5×5 循环盒式模糊(25 个采样平均)是单 Pass 里最廉价的近似模糊;之后叠加一层与 u_blur 成正比的颗粒噪声,模拟玻璃内部的霜晶散射。
效果 10:像素排序 / 故障艺术(Pixel Sort / Glitch Art)
像素在竖直或水平条带里自我重排,数字艺术质感。适合科技/创意品牌:
uniform sampler2D u_tex;
uniform float u_intensity; // 0-1
void main() {
vec2 uv = v_uv;
// Random horizontal displacement per row
float row = floor(uv.y * 80.0);
float noise = fract(sin(row * 127.1) * 43758.5);
float displace = step(0.7, noise) * u_intensity * 0.1;
// Shift UV with RGB split
float r = texture(u_tex, uv + vec2(displace, 0.0)).r;
float g = texture(u_tex, uv).g;
float b = texture(u_tex, uv - vec2(displace * 0.5, 0.0)).b;
fragColor = vec4(r, g, b, 1.0);
}
按行取样(uv.y * 80 划分 80 条扫描带),只有噪声超过 0.7 的「幸运行」才被位移(step(0.7, noise)),从而形成稀疏故障感而非整屏撕裂;u_intensity 从 GSAP 代理驱动,可在时间线里只在一瞬间推高制造抖动。
自建任意自定义效果的通用配方
以上片元着色器本质都是模板。规律永远是四步:
- 用
drawElementImage捕获你的 HTML 内容(复用本文顶部的样板); - 把捕获后的 canvas 上传为 WebGL 纹理;
- 写一个读取该纹理、输出修改后颜色的片元着色器;
- 通过
onUpdate用 GSAP 驱动着色器 uniform。
基于此,任何 ShaderToy、The Book of Shaders、CodePen 等渠道的 GLSL 效果都可以迁移到 HyperFrames:
- 找一个你中意的效果(搜索 "GLSL [效果名]" 或浏览 shadertoy.com);
- 复制其片元着色器;
- 把
iResolution换成vec2(1920.0, 1080.0),把iTime换成你的u_timeuniform; - 追加
uniform sampler2D u_tex;用于捕获到的内容纹理; - 把各 uniform 接到 GSAP 代理值上。
平面之外,还有更多几何形态:
SphereGeometry—— 把内容贴到球体上(世界地图、全球触达);CylinderGeometry—— 内容贴到旋转圆柱上(轮播/滚动感);TorusGeometry—— 内容环绕圆环(无限、循环);BoxGeometry—— 内容贴到 3D 盒子上(产品包装、骰子);- GLTF 模型 —— 把内容作为屏幕纹理贴到手机、笔记本、显示器上(参见
vfx-iphone-device,即把 GLTF 手机/笔记本与 HTML-in-canvas 屏显组合的 registry block)。
后期链叠加组合(Three.js EffectComposer 多 Pass):
- Bloom + 胶片颗粒 = 电影感;
- Bloom + 色差 = 镜头感;
- 景深 + 暗角 = 聚焦注意力;
- 胶片颗粒 + 扫描线 = 复古风;
- Pass 可以无限叠加——想要多少就加多少。
「你并不被此处的效果列表限制。」HTML-in-Canvas API 提供的是原料(任意 HTML 渲染成的纹理),WebGL/Three.js 提供的是对这份原料的无限创作自由。
选型表:HTML-in-Canvas 还是标准 GSAP?
| 场景 | 选型 | 理由 |
|---|---|---|
| Hero 产品截图展示 | HTML-in-Canvas(3D 旋转 + Bloom) | 让平面 UI 拥有电影质感 |
| 功能列表 / 数据统计 | 标准 GSAP | 内容导向,不需要 3D |
| CTA / 品牌揭晓 | HTML-in-Canvas(传送门或磁吸) | 让瞬间值得记住 |
| 社会证明 / Logo 墙 | 标准 GSAP | 有序级联,信任感是平稳的 |
| 幕间转场 | HTML-in-Canvas(粉碎) | 戏剧化的幕断 |
| 背景氛围 | HTML-in-Canvas(液体表面) | 高级环境感 |
| 快速功能卡片 | 标准 GSAP | 速度优先,3D 会拖慢节奏 |
表格背后的取舍逻辑与 hyperframes-animation 技能 的运行时选择哲学一致:GSAP 承担 95% 的运动工作(时间线、变换、缓动、交错都足够),HTML-in-Canvas 是有意的、稀缺的、用于制造对比的重型武器。滥用会同时伤害节奏与性能。
仓库内的对应资源与进一步阅读
- 本文档源文件:html-in-canvas-patterns.md(skill 路由表中标注为「HTML-as-texture + WebGL/GLSL post-fx」入口)
- 运行时加载方式汇总:hyperframes 能力清单 中「HTML-in-canvas」与「Animation engines」两节(六个确定性运行时适配器的 seek 契约、Three.js 通过
window.__hfThreeTime+hf-seek事件驱动) - WebGPU 变体与渲染环境前提:typegpu.md(liquid-glass 等 block 的浏览器要求、
hf-seek事件契约) - 可安装的 VFX blocks 清单:registry 发现文档 中 "VFX (6)" 与 "Liquid Glass (7)" 两个分类表(
vfx-shatter、vfx-magnetic、vfx-portal、vfx-liquid-background、vfx-iphone-device等,可经 registry 机制安装到你的组合工作区) - 相邻通用技法:techniques.md(无 3D 依赖的 Canvas 2D、逐字动力学排版、Lottie、可变字体轴等 13 种技法与「何时用什么」映射表,HTML-in-Canvas 是它们之上的重型补充)
- OpenMontage 侧接入方式:skills/core/hyperframes.md(
render_runtime决策矩阵、HyperFrames 项目工作区布局、lint/validate/render 验证协议,以及--workers 1等实战避坑记录)
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00