关于 threejs-particle-morph
threejs-particle-morph 是用于网页视觉效果的 Three.js 技能,可让粒子随滚动从随机分布聚合成图像或标志,并在反向滚动时散开。它通过采样源图像像素生成粒子目标位置,并提供可调整的粒子参数。
主要功能
- 01从图像像素生成粒子目标
- 02粒子随滚动进度聚合
- 03反向滚动时粒子散开
- 04可配置粒子数量和采样参数
- 05提供资源清理方法
适用场景
- 01让作品集首屏标志随滚动聚合
- 02制作粒子组成图像的滚动效果
- 03让图像在反向滚动时解体
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill threejs-particle-morph安装后在 Claude Code 中输入/threejs-particle-morph确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/demo.cssdemo.htmldemo.js详细介绍
技能:threejs-particle-morph
纯 Three.js 组件——数千个粒子随机散布在 3D 空间中,用户滚动页面时逐渐汇聚成从像素中采样的图像,反向滚动时再次散开。
WHAT
用途与背景
- **用途:**构建一个 Three.js 粒子云(
THREE.Points+BufferGeometry),根据页面滚动进度,从随机位置汇聚成图像/Logo,并在反向滚动时再次散开——优先原样复制原始版本,assets/中的 vanilla 版本作为备用。 - 触发条件(适用场景):
- Hero 区域/作品集需要“图像由粒子拼合而成”的滚动效果。
- 用户提到“粒子图像”“粒子汇聚成图像”“点云变形”“three.js 粒子滚动”。
- **不适用场景:**不要用于缩放模糊式的平滑图像过渡效果(这是
blur——在单个平面上使用着色器,没有离散粒子),也不要用于不需要 WebGL 的轻量滚动效果(这是scroll-effects)。不要重写作者的原始代码。
思维模型
源图像 → 隐藏 canvas(sampleResolution)→ getImageData + stride + alphaThreshold → targets(+ 颜色)· starts 随机 → 滚动 → targetProgress → progress(阻尼)→ position = lerp(starts, targets, progress) → PointsMaterial 渲染。
输入与输出约定
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| 输入 | 源图像/Logo(imageUrl) | 否 | 缺失时 → demo 会在隐藏 canvas 上自动绘制示例文字以供采样 |
| 输入 | particleCount、particleSize、sampleResolution、alphaThreshold | 否 | ParticleMorph 的初始化参数 |
| 输入 | 项目限制(是否允许引入外部库?) | 是 | 决定使用原始版本(默认)还是 assets/ vanilla 版本 |
| 输出 | 项目中的组件 | 是 | 原始文件原样复制(保留署名注释),或适配后的 assets/demo.*;包含 destroy() |
| 输出 | 验证证据 | 是 | progress 0/0.5/1 时的截图 + 无报错的 console |
规则与能力
- RULE-01(MUST):性能:粒子越多,CPU(每帧循环写入
position)和 GPU 负载越高。中端设备的建议安全范围:≤ 5,000–10,000 个粒子。增加particleCount前,先用 DevTools Performance/FPS meter 测量,不要猜测——尤其是在为大图/细节丰富的图像增加粒子时(提高sampleResolution也会增加init()时的采样成本,尽管只运行一次)。 - RULE-02(MUST):Three 库(
three)必须通过 npm 安装(npm install three),并在生产环境中通过 bundler 导入——demo 使用importmap指向 CDN(unpkg.com/[email protected]),仅用于无需 bundler 即可独立运行;不要使用像blur之前那样固定到旧版本的 CDN。接入真实项目时:删除<script type="importmap">,运行npm install three,并将import * as THREE from 'three'改为通过 bundler 导入。 - RULE-03(SHOULD):
_buildSamples()中的alphaThreshold/亮度过滤会排除透明/暗色背景像素——透明背景图像(PNG Logo)的效果比纯色背景图像更整洁;纯色背景图像会连背景一起采样,除非先裁剪。 - RULE-04(MUST):使用
playwright-verify验证——在 progress 0/0.5/1 时截图(滚动到这 3 个位置),并检查 console(WebGL context 错误通常不会提示,也不会抛出明确的 JS 错误)。 - RULE-05(MUST):优先使用原始版本——原样复制文件,不要重写;保留首行的署名注释;只修改内容/图像/颜色;通过 HTTP 在本地运行,不要使用
file://。 - 能力:从公开的外部仓库读取/复制资源;在项目中编写前端文件;通过 HTTP 运行页面,并使用无头浏览器截图/读取 console。
失败边界
- 无法打开原始链接/无法引入外部库 → 使用
assets/vanilla 版本(分支 B01)。 - 增加
particleCount后 FPS 下降 → 阻止继续增加:重新测量,并保持在 ≤ 5,000–10,000 个粒子。 - Console 报告 WebGL context 错误(通常不会提示)→ 验证失败,不得交付。
- 纯色背景图像会连背景一起采样 → 先裁剪,或使用透明背景 PNG。
HOW
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/下一步 |
|---|---|---|---|---|---|
| W01 | 判断 | 项目约束 | 选择来源:原始版本 Rheinmir/uiux-asset(默认)还是 vanilla assets/ | 已选来源 | 不引入外部库 → B01 |
| W02 | 效果 | 运行链接 + 代码 | 打开运行链接查看正确效果 → 将完整 .html 文件(+ _assets/)复制到项目中 | 项目中的文件,保留署名注释 | — |
| W03 | 效果 | 文件 | 只修改内容/图片/颜色(或 vanilla 版本中的 imageUrl/particleCount/particleSize) | 已适配的组件 | — |
| W04 | 确定性 | 通过 HTTP 访问的页面 | 使用 playwright-verify 验证:在 0/0.5/1 进度时截图,读取控制台 | 3 张截图 + 控制台无错误 | WebGL/FPS 错误 → 修复,重复 W03 |
| W05 | 效果 | 实际项目 | 接入生产环境:删除 importmap,npm install three,通过打包器导入 | 构建成功运行 | — |
各步骤详情(W01–W05 的事实依据):
原始版本 — 优先使用(复制完整文件,不要重写)
作者的原始代码,原样不动(公开的 CodePen = MIT),位于外部仓库 Rheinmir/uiux-asset — 在线运行 gallery:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
流程:打开运行链接查看正确效果 → 将完整 .html 文件(如果文件指向 _assets/ 文件夹,也一并复制)复制到项目中 → 保留首行署名注释 → 只修改内容/图片/颜色。通过 HTTP 在本地运行,不要使用 file://。
如果需要完整离线版本,可运行 git clone --depth 1 https://github.com/Rheinmir/uiux-asset。
| 原始 Pen — 作者 | 链接 |
|---|---|
| Scroll-Driven Particle WebGL Image Matrix — ycw | 运行 · 代码 |
| GSAP ScrollSmoother and Three.js — cmalven | 运行 · 代码 |
| GSAP ScrollTrigger Disintegration Effect — dev_loop | 运行 · 代码 |
此技能中的 assets/ 只是自行编写的精简 vanilla 版本 — 仅在项目禁止引入外部库时使用;默认使用上面的原始版本。
资源
assets/demo.html + assets/demo.css + assets/demo.js — 阅读后进行适配(初始化 ParticleMorph 时修改 imageUrl、particleCount、particleSize)。如果未传入 imageUrl,Demo 会在隐藏的 canvas 上自行绘制示例文字并进行采样,因此无需图片/网络即可立即运行。
机制
- 图片采样:将源图片(或手绘 canvas)绘制到尺寸为
sampleResolution的隐藏<canvas>上,读取getImageData()。按步长遍历像素(根据目标particleCount与总像素数计算stride)——不要采样所有像素,开销太大——保留亮度足够且不透明的像素(alphaThreshold)作为目标坐标和 RGB 颜色。 - 粒子:每个采样坐标对应
THREE.Points中的 1 个粒子(BufferGeometry属性position)。初始位置(starts)随机分布在一个较大的空间块中(“爆炸”效果),目标位置(targets)是采样得到的像素坐标,分别存储在两个独立的Float32Array中(不是两个 GPU attribute)。 - 滚动 → 进度:只使用一个 scroll 监听器,并通过 rAF 限制调用频率(不使用 GSAP/ScrollTrigger——演示刻意采用原生写法,避免与
gsap-scrolltrigger-pin的依赖重复),根据scrollY / (scrollHeight - innerHeight)计算targetProgress。 - 每帧插值:
progress逐渐平滑到targetProgress(简单阻尼,不直接动画scrollY),然后对每个元素执行position[i] = lerp(starts[i], targets[i], progress),并设置geometry.attributes.position.needsUpdate = true。 - 颜色 + 大小:
PointsMaterial({ size, sizeAttenuation: true, vertexColors: true })——通过静态colorattribute 保留原图颜色(构建时设置一次,不随 progress 改变)。 - 清理:编写为带有
destroy()的类——取消requestAnimationFrame,移除scroll/resize监听器,调用geometry.dispose()、material.dispose()、renderer.dispose()。
分支
| ID | 类型 | 条件 | 行为 | 跳过 / 失败 | 汇合 |
|---|---|---|---|---|---|
| B01 | conditional_required | 项目不得引入外部库 | 使用 assets/demo.html + assets/demo.css + assets/demo.js,调整 ParticleMorph 的初始化参数 | — | W04 |
| B02 | capability_optional | 需要完整的离线资源包 | git clone --depth 1 https://github.com/Rheinmir/uiux-asset | 可联网访问 gallery 时 → 跳过 | W02 |
验证与停止条件
通过实际运行进行检查:使用 playwright-verify 截取 3 个滚动位置的画面并读取 console(WebGL 错误通常不会提示);通过 DevTools Performance/FPS meter 测量性能,不要凭猜测。目视检查:progress 为 1 时图片正确汇聚,为 0 时粒子散开。3 张截图正确且 console 无错误时停止。
示例
- **正向示例:**作品集 hero 需要在滚动时让 PNG logo 汇聚 → 从 gallery 复制完整的
scroll-driven-particle-webgl-image-matrix.html,保留署名注释,更换 logo 图片 → 截取 0/0.5/1 时的画面,确认粒子散开 → 半成像 → 完整 logo,且 console 无错误。 - **边界/失败:**项目禁止使用外部 CDN 库 → 使用
assets/demo.js,particleCount为 30.000 导致 FPS 下降 → 根据测量结果降至 ≤ 10.000,然后重新验证。 - **边界:**用户想要在一个 plane 上通过 zoom-blur 效果从图片 A 过渡到图片 B → 不要使用此 skill,改用
blur。