Image · Type · Motion · Sound

把图片、文字、 动效与声音 放进同一个页面

纯静态构建,无外部字体、无第三方脚本。首屏只有 ~30KB 关键 CSS/JS, WebGL 与音频可视化按需加载,弱网下也能先看到内容。

Capabilities

这一页能承载什么

六块能力各自独立、都能单独删掉而不影响其他部分。想加内容就加 Markdown, 想加效果就加一个 island,不需要动整体架构。

图片

构建期转 AVIF/WebP 并按视口出多尺寸;首屏只加载海报,其余全部 lazy。源图放进 src/assets/ 即可。

文字

Markdown + 内容集合(schema 校验),图文内容与组件解耦,改文字不用碰代码。

动效

WebGL 首屏流场 + GSAP 滚动长卷,全部动态 import、可降级、受 prefers-reduced-motion 控制。

音频

HTML5 音频按需 Range 流式加载,Web Audio 实时频谱,AudioContext 在用户手势内创建。

动态接口

同构的 fetch handler:Cloudflare Pages Functions + KV,或任意 Node 机器上的同一份逻辑。

国内可达

Cloudflare 主线 + 境外备份出口,DNS 分线路解析;不备案方案的可达性缓解措施见 docs/。

Motion

复杂动效,但要能降级

动效库全部走动态 import()。默认情况下这段页面只是普通的纵向图文列表; 只有在支持且用户允许动效时,才会加载 GSAP 并把轨道变成横向长卷。

↓ 继续滚动,画面会横向推进

01
GSAP · scrub

固定视口 + 横向轨道

滚动被"劫持"成横向位移:GSAP ScrollTrigger 把纵向滚动距离映射为横向 translateX,容器 pin 住不动。

02
astro:assets

构建期出 AVIF

同一张源图在构建时生成多尺寸 + 现代格式,浏览器按视口挑最合适的一张,首屏之外全部 lazy。

03
a11y first

可降级的动效

prefers-reduced-motion 打开时,这段脚本根本不会被加载,横向轨道退化成普通纵向列表。

Sound

音频与实时频谱

预加载策略是 none:页面打开时一个字节的音频都不下。 点击播放后才开始加载音频,同时用 Web Audio 的 AnalyserNode 画出实时频谱。

Ambient Drift (示例 · CC0) 0:00 / 0:00

替换方式:把音频丢进 public/audio/,改上面的 src 即可。 注意:Cloudflare Pages 对 Range 请求返回 200(整体下载),拖动进度条需要等文件下完, 所以单文件建议控制在几 MB 以内;要做真正的流式播放,就把音频放到 Cloudflare R2 或自有服务器上(仓库里的 server/index.mjs 已正确实现 206 分片响应)。