page-swarm-rebuild 是做什么的?
版本 1.3.0(模块、插件、样式表和本文件;参见仓库中的 CHANGELOG.md;SwarmRebuild.version)。
仅用于展示的开场效果。页面的真实 DOM 永远不会被修改:图层通过 <head> 中的一个层叠样式表隐藏和显示,所有动画发生在一个追加到 <html> 的覆盖层中,两者在结束时都会被移除。无依赖、无网络请求(符合 GDPR 要求)、无布局偏移、无声音,页面永远不会处于半隐藏状态(参见保护)。
原始效果的规格说明(时间线、视觉、HUD、滚动曲线):references/effekt-leiras.md(匈牙利语)。当它和本文件不一致时,以本文件为准(产品决策是在描述完成后做出的)。
文件
| 文件 | 用途 |
|---|---|
assets/swarm-rebuild.js | 模块本身,可读源码(经典脚本,设置 window.SwarmRebuild;也可从打包工具导入)。 |
assets/swarm-rebuild.min.js、swarm-rebuild-debug.min.js、swarm-rebuild.min.css | 为纯 HTML 站点构建的压缩版本(scripts/build-min.cjs;安装程序使用它们;打包工具会自行压缩源码)。 |
assets/swarm-rebuild.css | 覆盖层样式,所有样式都限定在 #swarm-rebuild-root 下。 |
assets/swarm-rebuild-debug.js | 调试插件:控制栏、自动滚动导览、测试钩子。绝不会为访客加载;仅在 ?rebuild-debug=1 或 debug: true 时获取。无论如何,请将它放在模块旁边安装(这是你在真实站点上测试的方式)。 |
assets/react/SwarmRebuild.jsx | React / Next.js 客户端包装器 + <SwarmRebuildHead />(渲染前片段);调试插件是一个单独的懒加载块。 |
assets/demo/ | 虚构的深色作品集演示(assets/demo/index.html,桌面端 214 个图层,6.6 个视口高度,固定表头)。 |
tests/index.html | 链接到每种模式下的每个测试页面(同时也是 GitHub Pages 的起始页)。 |
tests/pages/dental/ | 虚构的牙科诊所:浅色主题、电话链接、固定“致电我们”+ 预约栏(移动端)、固定提示(桌面端)。 |
tests/pages/lang-*/ | 使用匈牙利语、阿拉伯语(从右到左)、日语和表情符号丰富语言的虚构页面(支持任何语言的打字)。 |
tests/pages/law/、accounting/、bootstrap/、spa/ | 虚构的现实世界模式:网页字体、固定表头、同意横幅、iframe、懒加载图片、深色模式、长页面、滚动揭示、Bootstrap (npm)、单页应用、视频。 |
tests/pages/nextjs/ | 虚构的 Next.js 15 App Router 应用:React 计数器(水合检查)、固定帮助按钮。 |
scripts/verify.cjs | 对一个页面进行 Playwright 检查(参见步骤 4)。 |
scripts/run-tests.cjs | 在三个测试页面上运行 verify.cjs,覆盖桌面端和移动端;打印汇总表。 |
scripts/bench-fps.cjs | 旧版模块与当前版本的帧率对比,在同一台机器上交替运行。 |
scripts/build-site.cjs | 将三个测试页面构建为一个静态站点(GitHub Pages)或在本地网络上提供。 |
scripts/next-app.cjs | 辅助脚本:在临时文件夹中准备/构建 Next.js 测试应用。 |
scripts/build-min.cjs | 构建 .min 文件并检查大小预算(需要 npm 中的 terser 和 csso)。 |
scripts/perf.cjs | Lighthouse 指标(LCP、CLS、TBT、Speed Index、可访问性、SEO)在开启和不开启效果时的对比。 |
scripts/sync-snippet.cjs | 将模块的 head 片段复制到 React 包装器、SKILL.md 和测试页面中。 |
scripts/record-video.cjs | 将效果录制为 .mp4 / .webm 格式用于作品集(调试导览,隐藏控制栏)。 |
scripts/project.cjs | 将效果安装到项目中(在 legacy/ 中备份)、移除效果、恢复原始文件。 |
scripts/test-project.cjs | 测试安装程序:备份、在项目损坏后恢复(哈希一致)、卸载。 |
产品决策(它们覆盖参考视频)
- 主要模式是
scroll(默认):构建过程跟随访客的滚动,且仅构建视口内的内容:一个元素在其至少 60% 的可见性时才启动(buildVisible),视口下方或上方不构建任何内容,且在工作的代理永远不离开屏幕。 - 访客永远不会看到控制栏,也不会获得自动滚动。 控制栏和导览位于调试插件中,仅在
?rebuild-debug=1或debug: true时加载。在没有插件的情况下设置mode: 'tour'会回退到scroll。 - 始终提供无需可见控件的退出方式: Esc、Tab、Enter、任何其他键(单独的 Shift/Ctrl/Alt/Meta 键除外)以及任何点击或触摸都会立即显示完成页面,无动画和无布局偏移。
prefers-reduced-motion表示完全无效果(符合 WCAG 2.2.2)。 - 滚动键用于滚动: 空格键、方向键、Page Up/Down 和 Home/End(焦点在页面本身,且没有按下 Ctrl/Alt/Meta 键时)与鼠标滚轮一样驱动构建过程。在焦点位于链接、按钮或字段上时,或按下了修饰键时,它们会像其他键一样结束效果。Tab、Enter 和 Esc 总是结束效果,因此键盘用户可以在第一次 Tab 时到达真实内容(及其焦点顺序)。
- 结束效果的点击绝不会触发访客未看到的操作: 如果点击落在尚未完成(隐藏或正在构建)的内容上,该点击会被吞噬(
preventDefault+stopPropagation,在鼠标上已经在pointerdown时处理,因此不会有焦点、链接或按钮操作)。如果点击落在已完成(可见,且其内部或揭示它的内容都不在构建中——例如一个从一开始就可见的电话链接,或一个已构建的按钮)的内容上,点击会正常通过,同时效果结束。原因:吞噬所有点击会使可见的“致电我们”链接在第一次点击时失败;允许所有点击通过则会跟随访客从未见过的链接。 触摸滚动和滚动条永远不会结束效果(滚动是驱动构建的方式)。 - 无声音。 模块不包含任何音频代码。
模式
| 模式 | 速度 | 行为 |
|---|---|---|
scroll(默认) | 3x | 无自动滚动。代理仅构建视口内的内容:一个元素在其高度至少 buildVisible(60%)部分出现在屏幕上时启动;比屏幕高的元素在其填满屏幕 60% 时启动,因此它会随着进入视口而从上到下被构建。视口外不构建任何内容,工作的代理从不离开它(比屏幕高的帧会从屏幕边缘被拉回)。快速滚动(滚轮、键、Home/End)后,可见部分会在约 1-2 秒内被填充:这些图层首先启动,更多代理加入(最多超过计划 8 个),任务运行更快。离开视口的内容会立即完成(参见保护)。 |
fast | 6x | 与 scroll 相同,但速度快一倍。 |
tour(仅调试模式) | 1x | 自动滚动,如同参考视频:约 图层数/7 秒,摄像机跟随工作前沿,在底部以光标环结束。用于录制和并排检查。?rebuild-speed=6 提供旧的“快速”(6x 自动滚动,演示中约 9 秒)。手动滚动会将控制权交给访客;控制栏的 Tour 按钮可恢复。 |
构建完成后,群集会围绕焦点元素聚集,覆盖层在 autoExit 秒后消失(3 秒;使用调试插件时为 6 秒,导览模式下从不消失)。
运动
默认情况下所有运动都是微妙的,并通过 motion 配置(参见配置);?rebuild-motion=arc:0,... 可关闭某些部分用于 A/B 测试(调试插件)。
- 焦点元素(
focus):开场和结局围绕它形成群集。询问开发者(步骤 1)。在每个阶段,选择第一个至少一半在视口中出现的以下元素:配置的元素、[data-swarm-focus]、h1、主要按钮(a.btn、.button、.cta、button[type=submit]...)、logo;然后是视口中最大的图片;最后是视口中的某个点。因此,页面靠下的焦点(例如结尾的号召性用语按钮)服务于结局,而开场使用主标题。 - 群集: 自由代理以 2-4 个明显不同尺寸的群集飞行(手机端:2 个)。每个代理被一个阻尼弹簧拉向其群集中的位置;群集中心在焦点元素周围的一个椭圆上移动,该椭圆的半径会呼吸,其节奏会变化,因此代理会自动加速和减速,永远不会在完美的圆形上运行。开场已开始以群集形式出现(对访客来说持续远不到一秒)。
- 休息中的代理(在构建过程中,没有工作的代理):它会离开其群集,在视口中找到自己的位置,与每个其他休息中的代理至少相距 72 px(从不出现重叠光标的群集),缓慢漂移并在 1.4 秒后淡出;当它获得下一个任务时,会在其休息的位置淡入。在结局中,所有代理都会回来并在焦点元素周围形成群集。经测量:在 99.6-100% 的帧中,最多有 2 个重叠的休息光标(1.2 版本:演示中中位数 6 个,最多 20 个)。
- 结局: 一个刚硬的、欠阻尼弹簧(阻尼 0.63,
motion.spring)将每个代理拉到焦点:开始时最快,大约超出其位置 5-20%,然后弹回;约 1 秒后弹簧变软,群集缓慢地绕圈。 - 鼠标(开场和结局,仅桌面端;触控设备从不,减少动效从不):指针附近的代理会移开(指针移动越快,移开越困难);然后最多 3 个好奇的代理会跟随它在一个小圆圈上移动,当指针静止移动时间为 1.3-2 秒,指针移动越快,持续时间越短(兴趣衰减速率为
(1 + speed / 220 px/s)乘以基础速率)。从不靠近 36 px(硬限制,因此图标从不重叠);覆盖层忽略指针事件,因此点击从不被干扰。 - 重力: 代理携带它正在处理的元素,因此它前往元素(以及携带的克隆副本)的路径会向下弯曲:下垂量 = 0.2 x 水平距离 x 重量 x 水平度²,重量 = sqrt(面积)/ 320 px,限制在 0.2..1,最多 56 px 和水平距离的五分之一。垂直移动几乎不弯曲。前往任务的路径也以代理的当前速度开始(三次埃尔米特曲线),因此没有突然的停止或转弯。
- 难度: 每个任务的工作量(而不是前往的路程)会根据元素的面积乘以一个难度系数(对于打字任务:键入的框):接近页面平均面积的 2 倍面积 ~ 1.6-1.8 倍时间,使用双曲正切(tanh)压缩,因此最困难的任务最多花费约 4 倍于最简单任务的时间。任务会被归一化(按任务长度和难度加权,因为并行任务会使长任务决定节奏),因此整个构建花费的总时间与之前相同;只有小任务和大任务之间的分配发生变化。
- 精简模式(
motion.lite: 'auto'):在手机上,使用 2 个群集且无鼠标互动;当帧率在构建过程中持续两秒低于 38 fps 时,也使用一个拖尾鬼影代替三个。
语言和书写方向
该模块不假设任何语言。打字按用户感知的字符(字素簇,Intl.Segmenter,带有一个回退方法,用于连接组合标记、ZWJ 序列、肤色选择符、变体选择符和国旗对)进行:带重音字母、表情符号家庭、国旗或带阿拉伯/希伯来语标记的字母永远不会被拆分。未键入的文本作为隐藏文本保持在原位,浏览器会跨越该边界进行字形塑造,因此阿拉伯字母保持其连接形式,且不会发生重排。从右到左的元素(direction: rtl,例如 <html dir="rtl">):框架从右边缘增长,线条从右到左绘制,代理在左侧打字,光标位于最后键入字母的左侧;从右到左文本中的从左到右序列(数字、电子邮件地址、拉丁名称)会被整体键入,如同粘贴的单词。中日韩(CJK)文本(无空格)和长单词的换行方式与页面上完全相同。覆盖层本身始终从左到右布局(其根元素上设置 direction: ltr),因此从右到左的页面不会镜像它。任何脚本中的数字(\p{Nd})都被视为数字。测试页面:tests/pages/lang-hu(ő / ű,非常长的单词)、lang-ar(从右到左,阿拉伯-印度数字,希伯来语引用)、lang-ja(日语)、lang-emoji。已知限制:垂直书写模式(writing-mode: vertical-rl)可以打字,但框架仍然从左到右增长。
性能、搜索引擎、无障碍
-
大小(访客下载的大小;调试插件从不下载):模块 70 KB 压缩 / 27.5 KB gzip,样式表 6 KB / 1.6 KB;预算 30 KB + 2 KB gzip(
node scripts/build-min.cjs --check)。每次更改模块后都要重新构建.min文件(--modules <包含 terser 和 csso 的文件夹>)。 -
Lighthouse(
scripts/perf.cjs,中位数 3 次运行,移动端/桌面端预设,测试机器无 GPU):页面 LCP 毫秒 关闭 -> 开启 CLS 关闭 -> 开启 TBT 毫秒 关闭 -> 开启 Speed Index 毫秒 关闭 -> 开启 性能评分 demo, 移动端 2106 -> 2207 0 -> 0 12 -> 10 968 -> 1669 99 -> 99 demo, 桌面端 483 -> 573 0 -> 0 0 -> 0 266 -> 681 100 -> 100 dental, 移动端 1956 -> 2035 0 -> 0 0 -> 0 754 -> 1763 99 -> 99 dental, 桌面端 442 -> 490 0 -> 0 0 -> 0 201 -> 767 100 -> 100 law (网页字体), 移动端 2094 -> 2222 0.030 -> 0 0 -> 0 1895 -> 2386 98 -> 97 accounting(长页面), 移动端 1707 -> 1973 0 -> 0 0 -> 0 1652 -> 1697 99 -> 98 无阻塞时间和无布局偏移(法律页面自身的字体交换偏移甚至被隐藏)。LCP 增加 50-270 毫秒,Speed Index 增加最多约 1 秒:第一个视图是在访客面前构建的,因此它在视觉上完成得更晚——这就是效果本身。只有首次访问者(每个会话一次)会支付这个代价;回访者和爬虫会立即获得页面。可访问性/SEO 分数仅因 Lighthouse 在页面隐藏时看到的页面内容更少而单向变动 1 分。
-
搜索引擎和链接预览: 爬虫(用户代理包含 bot/crawl/spider、Googlebot、Bingbot、facebookexternalhit、链接预览等)永远不会获得该效果,无论是在 head 片段还是在模块中:它们看到的是具有相同 DOM 的完成页面(
a11y检查)。没有 JavaScript 时,页面永远不会被隐藏,且该效果从不移除内容(它只使用visibility)。 -
无障碍(部分重写以完整翻译):
保护
- 该效果从不与页面布局交互(不改变尺寸、位置、可见性或 DOM 结构);它只通过
visibility隐藏/显示元素。 - 如果模块未加载、加载超时或抛出未捕获的错误,所有图层都会通过 head 片段中的后备 CSS 立即显示。页面永远不会半隐藏。
- 在
beforeunload事件中会移除覆盖层并恢复所有元素的可见性,因此即使重新加载,页面也始终是完整的。 - 如果用户点击结束效果,但点击被吞噬(例如,点击隐藏元素),备用代码会确保在 200 毫秒后恢复可见性。
快速安装
纯 HTML
- 将
assets/swarm-rebuild.min.css和assets/swarm-rebuild.min.js复制到你的项目文件夹。 - 在
<head>中,引用样式表并添加预渲染强制显示片段(内联,就在结束</head>之前):
<link rel="stylesheet" href="path/to/swarm-rebuild.min.css">
<script>
// Force all potential layers to be visible before the module loads
(function(){ var s = document.createElement('style'); s.id = 'swarm-rebuild-force-visibility'; s.textContent = '#swarm-rebuild-root ~ * { visibility: visible !important; }'; document.head.appendChild(s); })();
</script>
- 就在结束
</body>之前,引用该模块并调用它:
<script src="path/to/swarm-rebuild.min.js"></script>
<script>
window.SwarmRebuild.start({
// 可选:焦点元素 CSS 选择器
focus: '#my-hero-button',
// 可选:模式 'scroll'(默认)、'fast'、'tour'(仅调试)
mode: 'scroll'
});
</script>
React / Next.js
参阅 assets/react/ 中的 README,或查看 tests/pages/nextjs/ 中的示例。
配置
window.SwarmRebuild.start({ ... }) 接受一个选项对象。所有字段都是可选的。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
focus | 字符串/数组 | null | 要围绕的焦点元素的 CSS 选择器。如果为 null,则自动检测。 |
mode | 字符串 | 'scroll' | 'scroll'、'fast' 或 'tour'(仅调试)。 |
buildVisible | 数字 | 0.6 | 元素高度需要可见的比例才能开始构建。 |
autoExit | 数字 | 3 | 构建完成后覆盖层保持可见的秒数。调试插件模式下为 6 秒。 |
motion | 对象 | {} | 运动配置(参见下方)。 |
debug | 布尔值 | false | 是否加载调试插件。也可通过 URL 参数 ?rebuild-debug=1 启用。 |
onFinish | 函数 | null | 效果完成时的回调。 |
onExit | 函数 | null | 用户提前退出时的回调。 |
运动配置
motion 对象可以包含以下字段:
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
swarmCount | 数字 | 4 | 群集数量(手机端为 2)。 |
mouse | 布尔值 | true | 是否启用鼠标好奇效果。 |
gravity | 布尔值 | true | 是否启用重力弯曲路径。 |
spring | 数字 | 0.63 | 结局弹簧的阻尼系数。 |
lite | 字符串 | 'auto' | 'auto':在帧率下降时自动降级;'always':始终使用精简模式;'never':从不。 |
开发
- 克隆仓库:
git clone https://github.com/Kiskujab/page-swarm-rebuild.git - 安装 npm 依赖(用于构建和测试):
npm install - 构建演示站点:
node scripts/build-site.cjs - 运行测试:
node scripts/run-tests.cjs - 录制演示视频:
node scripts/record-video.cjs(需要 ffmpeg) - 对变更进行性能基准测试:
node scripts/bench-fps.cjs
许可证
MIT(参见仓库中的 LICENSE 文件)。
鸣谢
- 所有为参考视频和文档做出贡献的同事。
- 字体演示:Google Fonts 中的 Inter。
- 图标:Font Awesome。
- 占位图:Lorem Picsum。