Three.js 粒子特效实战③:粒子重组效果

Three.js 粒子特效实战①:用 Three.Quarks 打造拟真火焰喷射

Three.js 粒子特效实战②:用 Three.Quarks 实现喷泉效果

在很多特效网站上都有粒子重组的功能

使用quark能够很简单的实现基本的粒子重组效果

实现效果

实现源码

复制代码
基本环境搭建省略
// 使用canvas创建文字纹理的函数
function createTextTexture(text, options = {}) {
  // 默认配置
  const config = {
    width: 256,
    height: 256,
    fontSize: 48,
    fontFamily: "Arial",
    color: "#ffffff",
    backgroundColor: "transparent",
    textAlign: "center",
    textBaseline: "middle",
    ...options,
  };

  // 创建canvas元素
  const canvas = document.createElement("canvas");
  canvas.width = config.width;
  canvas.height = config.height;

  // 获取绘图上下文
  const context = canvas.getContext("2d");

  // 设置背景色
  if (config.backgroundColor !== "transparent") {
    context.fillStyle = config.backgroundColor;
    context.fillRect(0, 0, config.width, config.height);
  } else {
    // 透明背景需要清除画布
    context.clearRect(0, 0, config.width, config.height);
  }

  // 设置文字样式
  context.font = `${config.fontSize}px ${config.fontFamily}`;
  context.fillStyle = config.color;
  context.textAlign = config.textAlign;
  context.textBaseline = config.textBaseline;

  // 计算多行文本的绘制
  const lines = text.split("\n");
  const lineHeight = config.fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  const startY = (config.height - totalHeight) / 2 + config.fontSize / 2;

  // 绘制每一行文字
  lines.forEach((line, index) => {
    const y = startY + index * lineHeight;
    context.fillText(line, config.width / 2, y);
  });

  // 创建THREE.Texture
  const texture = new THREE.CanvasTexture(canvas);
  texture.needsUpdate = true;

  return texture;
}

// -----------------------------------------实现粒子效果---------------------------------------
let batchRenderer;
const addParticle = async () => {
  const group = new THREE.Group();

  // 创建粒子系统批量渲染器
  batchRenderer = new BatchedParticleRenderer();
  group.add(batchRenderer);

  const loader = new THREE.TextureLoader();
  const texture = loader.load("/src/assets/texture/point.png");
  const particles = new ParticleSystem({
    // 粒子动画的时间
    duration: 14,
    // 粒子是否循环播放
    looping: true,
    // 粒子开始的时间
    startLife: new ConstantValue(22),
    // 粒子开始的速度
    startSpeed: new ConstantValue(0),
    startSize: new IntervalValue(0.1, 0.2),
    startColor: new RandomColor(
      new THREE.Vector4(1, 1, 1, 1),
      new THREE.Vector4(1, 0.7, 0, 1)
    ),
    worldSpace: true,
    // 粒子最大的数量
    maxParticles: 2500,
    emissionOverTime: new ConstantValue(0),
    // 每次发射为0,但是初始就有2000个粒子
    emissionBursts: [
      {
        time: 0,
        count: new ConstantValue(2000),
        probability: 1,
      },
    ],
    shape: new CircleEmitter({
      radius: 20, // Radius of the circle
      thickness: 1, // 0 = edge only, 1 = full area
      arc: Math.PI * 2, // Emission arc angle (in radians)
      spread: 0,
    }),
    material: new THREE.MeshBasicMaterial({
      map: texture,
      transparent: true,
    }),
  });

  // 使用createTextTexture函数创建文字纹理,传入你想要的字符串
  const textTexture1 = createTextTexture("新中地教育", {
    width: 200,
    height: 50,
    fontSize: 26,
    color: "#ffffff",
    backgroundColor: "transparent",
  });

  const textTexture2 = createTextTexture("祝各位同学", {
    width: 200,
    height: 50,
    fontSize: 26,
    color: "#ffffff",
    backgroundColor: "transparent",
  });

  const textTexture3 = createTextTexture("工作顺利", {
    width: 200,
    height: 50,
    fontSize: 26,
    color: "#ffffff",
    backgroundColor: "transparent",
  });

  // 用这张图片的像素点来创建 TextureSequencer,阈值是透明度 0.2 的像素。
  //TextureSequencer 的三个参数分别是 scaleX、scaleY 和位置。
  const seq1 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-10, 0, 0));
  seq1.fromImage(textTexture1.image, 0.2);

  const seq2 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-20, 0, 0));
  seq2.fromImage(textTexture2.image, 0.2);

  const seq3 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-10, 0, 0));
  seq3.fromImage(textTexture3.image, 0.2);

  //   ApplySequences 的参数是每个粒子运动的时间间隔,间隔 0.001 秒移动下个粒子。
  const applySeq = new ApplySequences(0.001);
  applySeq.appendSequencer(new IntervalValue(1.0, 2.0), seq1);
  applySeq.appendSequencer(new IntervalValue(5.0, 6.0), seq2);
  applySeq.appendSequencer(new IntervalValue(9.0, 10.0), seq3);
  particles.addBehavior(applySeq);

  group.add(particles.emitter);
  batchRenderer.addSystem(particles);
  scene.add(group);
};
addParticle();
const clock = new THREE.Clock();
renderer.setAnimation(() => {
  const delta = clock.getDelta();
  renderer.render(scene, camera);
  // 渲染循环中更新粒子
  if (batchRenderer) {
    batchRenderer.update(delta);
  }
});

实现原理

这里同样是需要设置粒子的行为模式

核心api:

  • TextureSequencer :负责 定义粒子如何按照纹理图案排列 ,它分析纹理图像的像素信息,将粒子定位在纹理的不透明区域。
  • ApplySequences :作为粒子系统的一种行为(behavior), 负责控制何时应用哪个 TextureSequencer ,管理多个序列器及其应用的时间间隔。

具体用法

1.我们需要准备一张图片,这里可以用quark官方的文字图片或者自己用canvas去构造一个文字图,我这里选择了第二种,用canvas构造three的texture的方法在网上有很多

复制代码
// 使用createTextTexture函数创建文字纹理,传入你想要的字符串,返回一个THREE.Texture
const textTexture1 = createTextTexture("新中地教育", {
	width: 200,
	height: 50,
	fontSize: 26,
	color: "#ffffff",
	backgroundColor: "transparent",
});

2.得到材质之后,将材质构造TextureSequencer,然后设置这个Sequencer的运行时间在1s--->2s,最后将ApplySequences添加到行为模式中

复制代码
  const seq1 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-10, 0, 0));
  seq1.fromImage(textTexture1.image, 0.2);

  //   ApplySequences 的参数是每个粒子运动的时间间隔,间隔 0.001 秒移动下个粒子。
  const applySeq = new ApplySequences(0.001);
  applySeq.appendSequencer(new IntervalValue(1.0, 2.0), seq1);

  particles.addBehavior(applySeq);

3.这里我们有三个文字,可以分为三个TextureSequencer去构造

复制代码
  const textureParam={
    width: 200,
    height: 50,
    fontSize: 26,
    color: "#ffffff",
    backgroundColor: "transparent",
  }
  // 使用createTextTexture函数创建文字纹理,传入你想要的字符串
  const textTexture1 = createTextTexture("新中地教育", textureParam);

  const textTexture2 = createTextTexture("祝各位同学", textureParam);

  const textTexture3 = createTextTexture("工作顺利", textureParam);

// 用这张图片的像素点来创建 TextureSequencer,阈值是透明度 0.2 的像素。
  //TextureSequencer 的三个参数分别是 scaleX、scaleY 和位置。
  const seq1 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-10, 0, 0));
  seq1.fromImage(textTexture1.image, 0.2);

  const seq2 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-20, 0, 0));
  seq2.fromImage(textTexture2.image, 0.2);

  const seq3 = new TextureSequencer(0.15, 0.15, new THREE.Vector3(-10, 0, 0));
  seq3.fromImage(textTexture3.image, 0.2);

  //   ApplySequences 的参数是每个粒子运动的时间间隔,间隔 0.001 秒移动下个粒子。
  const applySeq = new ApplySequences(0.001);
  applySeq.appendSequencer(new IntervalValue(1.0, 2.0), seq1);
  applySeq.appendSequencer(new IntervalValue(5.0, 6.0), seq2);
  applySeq.appendSequencer(new IntervalValue(9.0, 10.0), seq3);
  particles.addBehavior(applySeq);

辅助函数:createTextTexture

复制代码
// 使用canvas创建文字纹理的函数
function createTextTexture(text, options = {}) {
  // 默认配置
  const config = {
    width: 256,
    height: 256,
    fontSize: 48,
    fontFamily: "Arial",
    color: "#ffffff",
    backgroundColor: "transparent",
    textAlign: "center",
    textBaseline: "middle",
    ...options,
  };

  // 创建canvas元素
  const canvas = document.createElement("canvas");
  canvas.width = config.width;
  canvas.height = config.height;

  // 获取绘图上下文
  const context = canvas.getContext("2d");

  // 设置背景色
  if (config.backgroundColor !== "transparent") {
    context.fillStyle = config.backgroundColor;
    context.fillRect(0, 0, config.width, config.height);
  } else {
    // 透明背景需要清除画布
    context.clearRect(0, 0, config.width, config.height);
  }

  // 设置文字样式
  context.font = `${config.fontSize}px ${config.fontFamily}`;
  context.fillStyle = config.color;
  context.textAlign = config.textAlign;
  context.textBaseline = config.textBaseline;

  // 计算多行文本的绘制
  const lines = text.split("\n");
  const lineHeight = config.fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  const startY = (config.height - totalHeight) / 2 + config.fontSize / 2;

  // 绘制每一行文字
  lines.forEach((line, index) => {
    const y = startY + index * lineHeight;
    context.fillText(line, config.width / 2, y);
  });

  // 创建THREE.Texture
  const texture = new THREE.CanvasTexture(canvas);
  texture.needsUpdate = true;

  return texture;
}
相关推荐
子兮曰2 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
前端小万2 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝2 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋2 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
卡布鲁2 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
C语言小火车2 天前
C/C++ 为什么需要编译器?
开发语言·c++
李少兄2 天前
JavaScript 隐式全局变量解析
javascript