前端圈在传的粒子特效,鼠标一过图片就散了,原来是这样做的

Particles Playground 是一个基于 Three.js 和自定义 GLSL 着色器的粒子特效库。它的核心能力是:把一张普通图片(或视频帧)转换为由成千上万个粒子组成的动态画面,并响应鼠标或触摸操作,在光标经过的地方留下拖尾效果。

这类效果在前端开发里并不罕见,但大多数实现要么依赖纯 JavaScript 逐帧计算粒子位置(性能开销大),要么视觉效果比较简陋。Particles Playground 的思路有所不同——它以图片纹理作为粒子颜色的来源,用 GLSL 着色器在 GPU 上完成粒子的位置计算和噪声扰动,同时引入 GSAP 处理场景切换动画,整体在视觉质量和性能之间取得了比较好的平衡。

效果是如何实现的

理解这个项目,需要先了解它的技术路径:

纹理采样决定粒子颜色

传统粒子系统里,每个粒子的颜色通常是单独设定的。Particles Playground 的做法是把目标图片作为 WebGL 纹理传入着色器,每个粒子根据自己的 UV 坐标从图片上采样颜色,因此粒子聚合后还原的是图片本身的内容。

GLSL 2D Simplex Noise 驱动粒子运动

粒子在静止状态下并非完全固定,而是通过 GLSL 实现的 2D Simplex 噪声(由 Ian McEwan 编写)不断产生轻微扰动,形成粒子"呼吸"的动态感。

鼠标拖尾通过 buildTail() 实现

鼠标移动时,buildTail() 方法会在光标经过的 UV 坐标位置写入影响力数据,着色器读取这个数据后,对附近的粒子施加额外偏移,形成粒子被"推开"然后缓慢归位的拖尾效果。

性能控制

库内置了 FPS 上限控制和鼠标事件节流,避免在高刷新率设备上造成不必要的 GPU 开销。抗锯齿也是按条件开启的,低端设备上会自动关闭。


配置参数说明

每个场景通过一个配置对象定义,主要参数如下:

参数类型说明
urlstring图片或视频的路径
thresholdnumber像素灰度阈值,控制哪些区域生成粒子
randomnumber粒子随机偏移强度,值越大粒子分布越松散
depthnumber深度效果强度,影响粒子 Z 轴偏移
maxDepthnumber最大深度值
sizenumber粒子大小
squarenumber粒子形状:0 = 圆形,1 = 正方形
quotestring场景附带的文字内容
texturestring设为 "video" 时使用视频纹理,否则为图片

threshold 是控制效果密度的关键参数:值越低,粒子越密集,近似还原图片原貌;值越高,只有亮度超过阈值的区域才生成粒子,画面会更稀疏、抽象。


安装与使用

前提条件:需要在 Node.js 环境下使用,并通过 Vite 启动开发服务器。

安装

git clone https://github.com/isladjan/particles-playground.git
cd particles-playground
npm install

启动开发服务器

npx vite

如果需要在局域网内其他设备(如手机)上预览触摸交互效果:

npx vite --host

构建生产版本

npx vite build
npx vite preview   # 预览构建结果

集成到自己的项目

核心 API 只有两步:实例化 Effect 类,然后调用 init()

基础用法

import { Effect } from './effect.js';
 
const texturesOptions = [
  {
    url: './images/photo1.jpg',
    threshold: 0.1,
    random: 0.5,
    depth: 1.2,
    maxDepth: 2.0,
    size: 2,
    square: 0,           // 圆形粒子
    quote: '这里是场景文字',
  },
  {
    url: './images/photo2.jpg',
    threshold: 0.2,
    random: 0.8,
    depth: 0.8,
    maxDepth: 1.5,
    size: 1.5,
    square: 1,           // 正方形粒子
    quote: '第二个场景',
  },
];
 
const effect = new Effect(texturesOptions);
effect.init();

使用视频纹理

把视频帧作为粒子颜色来源时,设置 texture: 'video'

{
  url: './videos/clip.mp4',
  texture: 'video',
  threshold: 0.15,
  random: 0.6,
  depth: 1.0,
  maxDepth: 1.8,
  size: 2,
  square: 0,
}

切换场景

项目内置 changeTexture(direction) 方法,传入方向值可以在多个场景之间切换,场景切换动画由 GSAP 驱动:

// 切换到下一个场景
effect.changeTexture(1);
 
// 切换到上一个场景
effect.changeTexture(-1);

适合哪些场景

Particles Playground 比较适合以下使用场景:

  • 创意型落地页:把主视觉图片或品牌形象转化为粒子效果,作为页面首屏
  • 作品集展示:将作品截图以粒子形式呈现,增加视觉层次
  • 互动艺术装置:结合触摸屏,配合 --host 参数在大屏设备上展示

需要注意的是,WebGL 依赖浏览器和硬件支持,在部分低端移动设备上性能表现可能不理想。库本身提供了 FPS 限制和节流机制,但对于非常低端的设备,可能还需要进一步降低粒子数量或关闭深度效果。此外,由于粒子效果基于图片纹理,图片分辨率和 threshold 的搭配需要多次调试才能达到预期效果。


写在最后

Particles Playground 的实现思路值得关注的地方在于:它没有在 JavaScript 层面逐帧计算每个粒子的位置,而是把这部分计算交给 GPU 的着色器完成,同时用视频/图片纹理承担了粒子着色的任务,减少了 CPU 的工作量。这个架构思路对于需要大量粒子的场景有比较明显的优势。

项目代码量不大,effect.js 是核心文件,对 Three.js 和 GLSL 有一定了解的开发者可以直接阅读源码理解实现原理,或者在此基础上扩展自己的交互逻辑。

GitHub 项目地址:https://github.com/isladjan/particles-playground (opens in a new tab)