threejs-particle-morph

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Skill: threejs-particle-morph

Skill: threejs-particle-morph

Component Three.js thuần — hàng nghìn particle rải ngẫu nhiên trong không gian 3D, hội tụ dần thành ảnh sample từ pixel khi user cuộn trang, tan rã lại khi cuộn ngược.
纯 Three.js component——数千个 particle 随机散布在 3D 空间中;随着用户滚动页面,particle 会逐渐汇聚成由像素采样得到的图像,反向滚动时则重新散开。

WHAT

功能说明

Purpose và context

目的与上下文

  • Purpose: dựng một particle cloud Three.js (
    THREE.Points
    +
    BufferGeometry
    ) hội tụ từ vị trí ngẫu nhiên thành ảnh/logo theo tiến độ cuộn trang và tan rã lại khi cuộn ngược — ưu tiên copy nguyên bản gốc, bản vanilla trong
    assets/
    là dự phòng.
  • Trigger (when to use):
    • Hero section/portfolio cần hiệu ứng "ảnh được ghép lại từ các hạt" khi cuộn tới.
    • User nói "particle image", "hạt hội tụ thành ảnh", "point cloud morph", "three.js particle scroll".
  • Non-goals: KHÔNG dùng cho hiệu ứng chuyển ảnh liền mạch kiểu zoom-blur (đó là
    blur
    — shader trên 1 plane, không có particle rời rạc) và KHÔNG dùng cho hiệu ứng cuộn nhẹ không cần WebGL (đó là
    scroll-effects
    ). Không viết lại code gốc của tác giả.
  • 目的: 构建一个 Three.js particle cloud(
    THREE.Points
    +
    BufferGeometry
    ),根据页面滚动进度从随机位置汇聚成图像/logo,反向滚动时重新散开——优先完整复制原始版本;
    assets/
    中的 vanilla 版本作为备用方案。
  • 触发条件(何时使用):
    • Hero section/portfolio 需要在滚动到此处时呈现“图像由 particle 拼合而成”的效果。
    • 用户说出“particle image(粒子图像)”“hạt hội tụ thành ảnh(粒子汇聚成图像)”“three.js particle scroll”“point cloud morph”“particle cloud biến hình theo scroll(随 scroll 变形的 particle cloud)”。
  • 非目标: 不用于类似 zoom-blur 的平滑图像切换效果(那是
    blur
    ——在 1 个 plane 上运行的 shader,不包含离散 particle),也不用于不需要 WebGL 的轻量滚动效果(那是
    scroll-effects
    )。不要重写原作者的代码。

Mental model

心智模型

ảnh nguồn → canvas ẩn (sampleResolution) → getImageData + stride + alphaThreshold → targets (+ màu) · starts random → scroll → targetProgress → progress (damping) → position = lerp(starts, targets, progress) → PointsMaterial render
.
源图像 → 隐藏 canvas(sampleResolution)→ getImageData + stride + alphaThreshold → targets(+颜色)· starts 随机 → scroll → targetProgress → progress(damping)→ position = lerp(starts, targets, progress) → 使用 PointsMaterial 渲染
。

Input và output contract

输入与输出约定

FieldRequired?Ý nghĩa
Inảnh/logo nguồn (
imageUrl
)
khôngthiếu → demo tự vẽ chữ mẫu lên canvas ẩn để sample
In
particleCount
,
particleSize
,
sampleResolution
,
alphaThreshold
khôngtham số khởi tạo
ParticleMorph
Inràng buộc dự án (được kéo lib ngoài không?)cóquyết định bản gốc (mặc định) hay
assets/
vanilla
Outcomponent trong dự áncófile gốc copy nguyên (giữ comment ghi công) hoặc
assets/demo.*
đã adapt; có
destroy()
Outbằng chứng verifycóảnh chụp progress 0/0.5/1 + console sạch
字段必填?含义
输入源图像(
imageUrl
)
否缺失时 → demo 会在隐藏 canvas 上绘制示例文字用于采样
输入
particleCount
、
particleSize
、
sampleResolution
、
alphaThreshold
否
ParticleMorph
的初始化参数
输入项目约束(是否允许拉取外部库?)是决定使用原始版本(默认)还是
assets/
中的 vanilla 版本
输出项目中的 component是完整复制的原始文件(保留署名 comment),或已适配的
assets/demo.*
;包含
destroy()
输出验证证据是progress 为 0/0.5/1 时的截图 + 无报错的 console

Rules và capabilities

规则与能力

  • RULE-01 (MUST): Hiệu năng: số particle càng nhiều càng nặng CPU (vòng lặp ghi lại
    position
    mỗi frame) lẫn GPU. Ngưỡng an toàn gợi ý cho máy tầm trung: ≤ 5.000–10.000 particle. Đo bằng DevTools Performance/FPS meter TRƯỚC KHI tăng
    particleCount
    , đừng đoán — nhất là khi thêm particle cho ảnh lớn/nhiều chi tiết (tăng
    sampleResolution
    cũng tăng chi phí sample lúc
    init()
    , dù chỉ chạy một lần).
  • RULE-02 (MUST): Ba container (
    three
    ) phải cài qua npm (
    npm install three
    ) và import qua bundler cho production — bản demo dùng
    importmap
    trỏ CDN (
    unpkg.com/three@0.169.0
    ) CHỈ để chạy độc lập không cần bundler, không dùng bản CDN cũ ghim version như
    blur
    từng làm. Khi wire vào dự án thật: xoá
    <script type="importmap">
    ,
    npm install three
    , đổi
    import * as THREE from 'three'
    sang import qua bundler.
  • RULE-03 (SHOULD):
    alphaThreshold
    /brightness filter trong
    _buildSamples()
    loại pixel nền trong suốt/tối — ảnh nền trong suốt (PNG logo) cho kết quả gọn hơn ảnh nền đặc (JPEG); ảnh nền đặc sẽ sample luôn cả nền trừ khi crop trước.
  • RULE-04 (MUST): Verify bằng
    playwright-verify
    — chụp ở progress 0/0.5/1 (cuộn tới 3 mốc), đọc console (WebGL context lỗi thường im lặng, không throw JS error rõ ràng).
  • RULE-05 (MUST): Bản gốc dùng TRƯỚC — copy nguyên file, đừng viết lại; giữ comment ghi công dòng đầu; chỉ sửa nội dung/ảnh/màu; chạy local qua HTTP, không
    file://
    .
  • Capabilities: đọc/copy asset từ repo ngoài công khai; ghi file front-end vào dự án; chạy trang qua HTTP + trình duyệt headless để chụp/đọc console.
  • RULE-01(必须):性能:particle 数量越多,CPU(每帧重写
    position
    属性的循环)和 GPU 负担越重。对于中等性能设备,建议的安全上限为 ≤ 5,000–10,000 个 particle。在增加
    particleCount
    之前,应先使用 DevTools Performance/FPS meter 实测,不要凭感觉判断——尤其是在为更大或细节更多的图像增加 particle 时(提高
    sampleResolution
    也会增加
    init()
    时的采样成本,尽管只执行一次)。
  • RULE-02(必须):名为
    three
    的 container 必须通过 npm 安装(
    npm install three
    ),并在 production 中通过 bundler import。demo 版本使用指向 CDN(
    unpkg.com/three@0.169.0
    )的
    importmap
    ,仅用于无需 bundler 即可独立运行;不要像
    blur
    过去那样使用锁定旧版本的 CDN。接入真实项目时:删除
    <script type="importmap">
    ,执行
    npm install three
    ,并将
    import * as THREE from 'three'
    改为通过 bundler import。
  • RULE-03(应该):
    _buildSamples()
    中的
    alphaThreshold
    /亮度滤镜会移除透明或深色背景像素——透明背景图像(PNG logo)的结果比实色背景图像(JPEG)更干净;实色背景图像会连背景一起采样,除非事先裁剪。
  • RULE-04(必须):使用
    playwright-verify
    进行验证——滚动至 progress 0/0.5/1 三个节点并截图,读取 console(WebGL context 错误通常不会明确抛出,也不会显示清晰的 JS error)。
  • RULE-05(必须):优先使用原始版本——完整复制文件,不要重写;保留文件首行的署名 comment;只修改内容、图像或颜色;通过 HTTP 在本地运行,不要使用
    file://
    。
  • 能力:从公开外部 repo 读取/复制 asset;将前端文件写入项目;通过 HTTP + headless 浏览器运行页面,以截图并读取 console。

Failure boundaries

失败边界

  • Không mở được link gốc / không kéo được lib ngoài → dùng
    assets/
    vanilla (nhánh B01).
  • FPS tụt khi tăng
    particleCount
    → blocked việc tăng thêm: đo lại, giữ ≤ 5.000–10.000 particle.
  • Console báo lỗi WebGL context (thường im lặng) → failed verify, chưa bàn giao.
  • Ảnh nền đặc sample cả nền → crop trước hoặc dùng PNG nền trong suốt.
  • 无法打开原始链接/无法拉取外部库 → 使用
    assets/
    中的 vanilla 版本(分支 B01)。
  • 提高
    particleCount
    后 FPS 下降 → 阻止继续增加:重新测量,并将数量保持在 ≤ 5,000–10,000 个 particle。
  • Console 报告 WebGL context 错误(通常不会明确抛出)→ 验证 失败,暂不交付。
  • 实色背景的图像会把背景一并采样 → 先裁剪,或改用透明背景的 PNG。

HOW

实施流程

Main workflow

主工作流

StepTypeInputsActionOutputs/exitFailure/next
W01judgmentràng buộc dự ánChọn nguồn: bản gốc
Rheinmir/uiux-asset
(mặc định) hay
assets/
vanilla
nguồn đã chọnkhông kéo lib ngoài → B01
W02effectlink chạy + codeMở link chạy xem đúng hiệu ứng → copy nguyên file
.html
(+
_assets/
) vào dự án
file trong dự án, còn comment ghi công—
W03effectfileChỉ sửa nội dung/ảnh/màu (hoặc
imageUrl
/
particleCount
/
particleSize
với bản vanilla)
component đã adapt—
W04deterministictrang qua HTTPVerify bằng
playwright-verify
: chụp progress 0/0.5/1, đọc console
3 ảnh + console sạchlỗi WebGL/FPS → sửa, lặp W03
W05effectdự án thậtWire production: xoá importmap,
npm install three
, import qua bundler
build chạy—
Chi tiết từng bước (nguồn chân lý cho W01–W05):
步骤类型输入操作输出/退出失败/下一步
W01判断项目约束选择来源:原始版本
Rheinmir/uiux-asset
(默认)或
assets/
中的 vanilla 版本
已选定来源无法拉取外部库 → B01
W02效果运行链接 + 代码打开实际运行的链接以确认效果 → 将原始
.html
文件(+
_assets/
)完整复制到项目中
项目中的文件,并保留署名 comment—
W03效果文件仅修改内容、图像或颜色(或在 vanilla 版本中修改
imageUrl
/
particleCount
/
particleSize
)
已适配的 component—
W04确定性验证通过 HTTP 访问的页面使用
playwright-verify
验证:截取 progress 0/0.5/1,读取 console
3 张截图 + 无报错的 consoleWebGL/FPS 错误 → 修复并重复 W03
W05效果真实项目接入 production:删除 importmap,执行
npm install three
,通过 bundler import
可正常运行的 build—
各步骤详情(W01–W05 的唯一事实来源):

Bản gốc — dùng TRƯỚC (copy nguyên file, đừng viết lại)

原始版本——优先使用(完整复制文件,不要重写)

Code gốc NGUYÊN VĂN của tác giả (CodePen public = MIT) nằm ở repo ngoài
Rheinmir/uiux-asset
— gallery chạy thật: https://rheinmir.github.io/uiux-asset/scroll-effects/ . Quy trình: mở link chạy để xem đúng hiệu ứng → copy nguyên file
.html
(+ thư mục
_assets/
nếu file trỏ tới) vào dự án → giữ comment ghi công dòng đầu → chỉ sửa nội dung/ảnh/màu. Chạy local qua HTTP, không
file://
.
git clone --depth 1 https://github.com/Rheinmir/uiux-asset
nếu cần cả bộ offline.
Pen gốc — tác giảLink
Scroll-Driven Particle WebGL Image Matrix — ycwchạy · code
GSAP ScrollSmoother and Three.js — cmalvenchạy · code
GSAP ScrollTrigger Disintegration Effect — dev_loopchạy · code
assets/
trong skill này chỉ là bản rút gọn vanilla tự viết — dùng khi dự án KHÔNG được kéo lib ngoài; mặc định dùng bản gốc ở trên.
作者原封不动的原始代码(CodePen public = MIT)位于外部 repo
Rheinmir/uiux-asset
——实际运行 gallery:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
流程:先打开实际运行的链接以确认正确效果 → 将原始
.html
文件(如果文件有引用,还包括
_assets/
目录)完整复制到项目 → 保留文件首行的署名 comment → 仅修改内容、图像或颜色。通过 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运行 · 代码
本 skill 中的
assets/
只是自行编写的精简 vanilla 版本——仅在项目不允许拉取外部库时使用;默认使用上述原始版本。

Assets

资源

assets/demo.html
+
assets/demo.css
+
assets/demo.js
— đọc rồi ADAPT (đổi
imageUrl
,
particleCount
,
particleSize
khi khởi tạo
ParticleMorph
). Demo tự vẽ chữ mẫu lên canvas ẩn để sample khi không truyền
imageUrl
, nên chạy được ngay không cần ảnh/network.
assets/demo.html
+
assets/demo.css
+
assets/demo.js
——读取后进行适配(ADAPT),在初始化
ParticleMorph
时修改
imageUrl
、
particleCount
、
particleSize
。当未提供
imageUrl
时,demo 会在隐藏 canvas 上自行绘制示例文字用于采样,因此无需图像或网络即可立即运行。

Cơ chế

实现机制

  • Sample ảnh: vẽ ảnh nguồn (hoặc canvas vẽ tay) lên
    <canvas>
    ẩn kích thước
    sampleResolution
    , đọc
    getImageData()
    . Duyệt pixel theo bước nhảy (
    stride
    tính từ
    particleCount
    mong muốn so với tổng pixel) — KHÔNG lấy hết mọi pixel, quá nặng — giữ pixel đủ sáng/không trong suốt (
    alphaThreshold
    ) làm toạ độ đích + màu RGB.
  • Particle: mỗi toạ độ sample = 1 particle trong
    THREE.Points
    (
    BufferGeometry
    attribute
    position
    ). Vị trí khởi tạo (
    starts
    ) random trong một khối không gian lớn ("nổ tung"), vị trí đích (
    targets
    ) là toạ độ pixel đã sample, lưu song song trong hai
    Float32Array
    riêng (không phải hai attribute GPU).
  • Scroll → progress: một scroll listener duy nhất, rAF-throttle (không GSAP/ScrollTrigger — bản demo cố tình viết thuần để không trùng phụ thuộc với
    gsap-scrolltrigger-pin
    ), tính
    targetProgress
    từ
    scrollY / (scrollHeight - innerHeight)
    .
  • Lerp mỗi frame:
    progress
    được làm mượt dần tới
    targetProgress
    (damping đơn giản, không phải animate trực tiếp
    scrollY
    ), rồi mỗi phần tử
    position[i] = lerp(starts[i], targets[i], progress)
    , set
    geometry.attributes.position.needsUpdate = true
    .
  • Màu + kích thước:
    PointsMaterial({ size, sizeAttenuation: true, vertexColors: true })
    — giữ màu ảnh gốc qua attribute
    color
    tĩnh (set một lần lúc build, không đổi theo progress).
  • Cleanup: viết dạng class có
    destroy()
    — huỷ
    requestAnimationFrame
    , gỡ
    scroll
    /
    resize
    listener, gọi
    geometry.dispose()
    ,
    material.dispose()
    ,
    renderer.dispose()
    .
  • 图像采样:将源图像(或手绘 canvas)绘制到尺寸为
    sampleResolution
    的隐藏
    <canvas>
    上,然后读取
    getImageData()
    。按步长(
    stride
    )遍历像素(该步长根据期望的
    particleCount
    与总像素数计算)——不要取用全部像素,否则负担过重——保留足够明亮/不透明(
    alphaThreshold
    )的像素,将其作为目标坐标和 RGB 颜色。
  • Particle:每个采样坐标对应
    THREE.Points
    中的 1 个 particle(
    BufferGeometry
    的
    position
    attribute)。初始位置(
    starts
    )随机分布于一个很大的 3D 空间(“爆散”),目标位置(
    targets
    )为采样得到的像素坐标;两者分别并行存储在两个
    Float32Array
    中(不是两个 GPU attribute)。
  • Scroll → progress:仅使用一个 scroll listener,并通过 rAF 节流(不使用 GSAP/ScrollTrigger——demo 特意采用纯原生实现,以免与
    gsap-scrolltrigger-pin
    重复依赖),根据
    scrollY / (scrollHeight - innerHeight)
    计算
    targetProgress
    。
  • 每帧 Lerp:
    progress
    通过简单 damping 平滑逼近
    targetProgress
    (而不是直接对
    scrollY
    做 animation),随后每个元素都执行
    position[i] = lerp(starts[i], targets[i], progress)
    ,并设置
    geometry.attributes.position.needsUpdate = true
    。
  • 颜色与尺寸:
    PointsMaterial({ size, sizeAttenuation: true, vertexColors: true })
    ——通过静态
    color
    attribute 保留原图颜色(build 时设置一次,不随 progress 改变)。
  • 清理:以包含
    destroy()
    的 class 形式实现——取消
    requestAnimationFrame
    ,移除
    scroll
    /
    resize
    listener,并调用
    geometry.dispose()
    、
    material.dispose()
    、
    renderer.dispose()
    。

Branches

分支

IDKindGuardHành viSkip / failureRejoin
B01conditional_requireddự án KHÔNG được kéo lib ngoàidùng
assets/demo.html
+
assets/demo.css
+
assets/demo.js
, ADAPT tham số khởi tạo
ParticleMorph
—W04
B02capability_optionalcần cả bộ offline
git clone --depth 1 https://github.com/Rheinmir/uiux-asset
có mạng tới gallery → skipW02
ID类型条件行为跳过/失败汇合点
B01conditional_required项目不允许拉取外部库使用
assets/demo.html
+
assets/demo.css
+
assets/demo.js
,并适配
ParticleMorph
的初始化参数
—W04
B02capability_optional需要完整离线版本
git clone --depth 1 https://github.com/Rheinmir/uiux-asset
可访问 gallery → 跳过W02

Validation và stopping

验证与停止条件

Kiểm bằng chạy thật:
playwright-verify
chụp 3 mốc cuộn + đọc console (WebGL lỗi thường im lặng); hiệu năng đo bằng DevTools Performance/FPS meter, không đoán. Review mắt: ảnh hội tụ đúng hình ở progress 1, tan rã ở 0. Dừng khi 3 ảnh đúng + console sạch.
通过实际运行进行验证:使用
playwright-verify
截取 3 个滚动节点并读取 console(WebGL 错误通常不会明确抛出);使用 DevTools Performance/FPS meter 测量性能,不要凭感觉判断。通过视觉检查:progress 为 1 时 particle 正确汇聚成图像,为 0 时正确散开。当 3 张截图正确且 console 无报错时停止。

Examples

示例

  • Positive: hero portfolio cần logo PNG hội tụ khi cuộn → copy nguyên
    scroll-driven-particle-webgl-image-matrix.html
    từ gallery, giữ comment ghi công, đổi ảnh logo → chụp 0/0.5/1 thấy hạt rải → nửa hình → logo đủ, console sạch.
  • Boundary/failure: dự án cấm lib CDN ngoài → dùng
    assets/demo.js
    ,
    particleCount
    30.000 làm FPS tụt → hạ về ≤ 10.000 theo đo, rồi verify lại.
  • Boundary: user muốn chuyển ảnh A→B kiểu zoom-blur trên một plane → không dùng skill này, chuyển
    blur
    .
  • 正向示例: hero portfolio 需要在滚动时让 logo PNG 汇聚成形 → 从 gallery 完整复制
    scroll-driven-particle-webgl-image-matrix.html
    ,保留署名 comment,替换 logo 图像 → 截取 0/0.5/1 三个状态,可见 particle 散开 → 图像呈现一半 → 完整 logo,且 console 无报错。
  • 边界/失败: 项目禁止使用外部 CDN → 使用
    assets/demo.js
    ;
    particleCount
    设为 30,000 后 FPS 下降 → 根据实测降至 ≤ 10,000,然后重新验证。
  • 边界: 用户希望实现 A→B 的 zoom-blur 图像切换,且效果位于单个 plane 上 → 不使用本 skill,改用
    blur
    。

Origin

来源