You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用React Three Fiber的Text组件实现粒子组成的动态文字?

实现自定义数量运动粒子组成文字的方案

核心实现思路

整体流程为:用Canvas预渲染文字提取像素坐标 → 生成对应数量的粒子并赋予目标位置 → 驱动粒子向目标位置运动(或添加动态效果)→ 实时渲染粒子

分步实现细节

1. 画布基础设置

创建两个Canvas:一个用于显示粒子的主画布,一个隐藏的离屏画布用于提取文字像素,避免主画布被干扰,同时高效获取像素数据。

<!-- 主画布 -->
<canvas id="particleCanvas"></canvas>
<!-- 隐藏的离屏画布 -->
<canvas id="textCanvas" style="display: none;"></canvas>

JS初始化画布:

const particleCanvas = document.getElementById('particleCanvas');
const ctx = particleCanvas.getContext('2d');
const textCanvas = document.getElementById('textCanvas');
const textCtx = textCanvas.getContext('2d');

// 适配窗口大小
particleCanvas.width = window.innerWidth;
particleCanvas.height = window.innerHeight;
textCanvas.width = particleCanvas.width;
textCanvas.height = particleCanvas.height;

2. 提取文字的粒子目标坐标

先在离屏画布绘制文字,遍历像素数据记录有颜色的位置作为粒子聚集目标点。若自定义粒子数与文字像素数不匹配,可通过复制目标点或在像素区域内生成随机点调整数量。

function getTextParticleTargets(text, fontSize, fontColor, particleCount) {
  // 清空离屏画布
  textCtx.clearRect(0, 0, textCanvas.width, textCanvas.height);
  // 设置文字样式
  textCtx.font = `${fontSize}px Arial`;
  textCtx.fillStyle = fontColor;
  textCtx.textAlign = 'center';
  textCtx.textBaseline = 'middle';
  // 绘制文字到画布中心
  textCtx.fillText(text, textCanvas.width / 2, textCanvas.height / 2);
  
  // 获取像素数据
  const imageData = textCtx.getImageData(0, 0, textCanvas.width, textCanvas.height);
  const data = imageData.data;
  const targets = [];
  
  // 遍历像素,提取有颜色的点(alpha通道>0)
  for (let y = 0; y < textCanvas.height; y += 2) { // 每隔2个像素采样,可调整间隔控制密度
    for (let x = 0; x < textCanvas.width; x += 2) {
      const index = (y * textCanvas.width + x) * 4;
      if (data[index + 3] > 0) { // alpha值大于0,说明是文字像素
        targets.push({ x, y });
      }
    }
  }
  
  // 调整目标点数量匹配自定义粒子数
  const adjustedTargets = [];
  const multiplier = Math.ceil(particleCount / targets.length);
  // 复制目标点并添加微小偏移,避免粒子完全重叠
  for (let i = 0; adjustedTargets.length < particleCount; i++) {
    const target = targets[i % targets.length];
    adjustedTargets.push({
      x: target.x + Math.random() * 4 - 2,
      y: target.y + Math.random() * 4 - 2
    });
  }
  return adjustedTargets.slice(0, particleCount);
}

3. 粒子类设计

每个粒子管理自身位置、速度、目标位置,通过缓动逻辑平滑向目标移动,同时可添加随机扰动增强动态感。

class Particle {
  constructor(x, y, targetX, targetY) {
    this.x = x;
    this.y = y;
    this.targetX = targetX;
    this.targetY = targetY;
    this.vx = (Math.random() - 0.5) * 4; // 初始随机速度
    this.vy = (Math.random() - 0.5) * 4;
    this.speed = 0.05; // 缓动系数,值越小移动越慢
    this.radius = Math.random() * 2 + 1; // 随机粒子大小
  }
  
  update() {
    // 计算目标方向向量
    const dx = this.targetX - this.x;
    const dy = this.targetY - this.y;
    // 缓动更新速度
    this.vx += dx * this.speed;
    this.vy += dy * this.speed;
    // 添加阻尼避免速度过快
    this.vx *= 0.95;
    this.vy *= 0.95;
    // 更新位置
    this.x += this.vx;
    this.y += this.vy;
    
    // 可选:添加随机抖动,增强动态效果
    if (Math.random() < 0.1) {
      this.vx += (Math.random() - 0.5) * 0.2;
      this.vy += (Math.random() - 0.5) * 0.2;
    }
  }
  
  draw(ctx) {
    ctx.fillStyle = '#333'; // 粒子颜色可自定义
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fill();
  }
}

4. 初始化粒子并启动动画

将提取的目标坐标分配给粒子,通过requestAnimationFrame循环更新渲染所有粒子。

let particles = [];

function initParticles(text, particleCount) {
  const targets = getTextParticleTargets(text, 120, '#000', particleCount);
  particles = targets.map(target => {
    // 让粒子从画布随机位置开始,实现聚合效果
    return new Particle(
      Math.random() * particleCanvas.width,
      Math.random() * particleCanvas.height,
      target.x,
      target.y
    );
  });
}

function animate() {
  // 半透明清空画布,实现粒子拖尾效果
  ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
  ctx.fillRect(0, 0, particleCanvas.width, particleCanvas.height);
  
  // 更新并绘制每个粒子
  particles.forEach(particle => {
    particle.update();
    particle.draw(ctx);
  });
  
  requestAnimationFrame(animate);
}

// 启动示例:生成5000个粒子组成"HELLO"
initParticles('HELLO', 5000);
animate();

5. 性能优化要点

  • 离屏Canvas预渲染:仅在文字改变时重新提取目标点,避免重复绘制文字。
  • 像素采样间隔:调整遍历像素的步长,控制目标点密度,减少计算量。
  • 批量绘制:粒子数量极大时,用ImageData直接操作像素,比逐个绘制arc更高效。
  • 窗口适配:监听resize事件,实时调整画布大小与粒子目标位置。

扩展效果建议

  • 粒子颜色渐变:根据粒子到目标的距离动态改变颜色。
  • 粒子大小变化:靠近目标时放大,远离时缩小。
  • 文字切换动画:切换文字时,让粒子平滑从当前位置移动到新目标点。
  • 交互效果:鼠标移动时,粒子受到排斥或吸引。

内容的提问来源于stack exchange,提问作者codingIsLove

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 03:38:23