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

基于Canvas与JavaScript实现按颜色分组的粒子动线效果

实现按颜色区分的粒子动线方案

我来帮你搞定这个需求——要让不同颜色的粒子形成独立动线,核心是给同色粒子设置统一的初始发射位置和运动基准参数,让它们的轨迹保持一致,自然区分开。下面是具体的实现思路和修改后的完整代码:

核心思路拆解

  1. 按颜色配置专属参数:为每种颜色单独定义发射点、速度范围、运动方向系数,确保同色粒子的运动“基准线”一致。
  2. 粒子分组初始化:不再随机给颜色,而是按比例创建对应颜色的粒子,同时标记它们的颜色归属,方便后续重置时匹配参数。
  3. 适配窗口变化:窗口大小改变时,同步更新颜色配置的发射位置,保证动线比例始终合理。

修改后的完整代码

<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<style>
canvas{ background-color: #000000 !important; }
body { margin: 0; overflow: hidden; }
</style>
<script>
(function() { 
  var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; 
  window.requestAnimationFrame = requestAnimationFrame; 
})(); 

var particleArr = [], 
    canvas = document.getElementById("canvas"), 
    ctx = canvas.getContext("2d"), 
    flakeCount = 700, 
    mouseX = -100, 
    mouseY = -100, 
    xMultiplier = 0.015;

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// 新增:为每种颜色定义专属运动配置
const colorConfigs = {
  red: {
    color: "rgba(215,88,69, 1)",
    startX: canvas.width,       // 红色粒子从画布右侧发射
    startY: canvas.height * 0.3, // 发射Y轴位置:画布30%高度处
    speedRange: [1, 4],         // 速度波动范围
    velXMultiplier: -0.02,      // X轴速度系数(负号表示向左运动)
    stepSizeRange: [0.02, 0.05] // 粒子横向波动的步长范围
  },
  blue: {
    color: "rgba(117, 161, 199, 1)",
    startX: canvas.width,       // 蓝色粒子同样从右侧发射
    startY: canvas.height * 0.7, // 发射Y轴位置:画布70%高度处
    speedRange: [0.8, 3.5],     // 比红色稍慢的速度
    velXMultiplier: -0.018,     // X轴速度稍缓
    stepSizeRange: [0.015, 0.04]
  }
};

// 辅助函数:生成指定范围内的随机数
function getRandomInRange(min, max) {
  return Math.random() * (max - min) + min;
}

function flow() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (var i = 0; i < flakeCount; i++) {
    var flake = particleArr[i], 
        x = mouseX, 
        y = mouseY, 
        minDist = 150, 
        x2 = flake.x, 
        y2 = flake.y;

    var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)), 
        dx = x2 - x, 
        dy = y2 - y;

    // 保留原有的鼠标碰撞避让逻辑
    if (dist < minDist) {
      var force = minDist / (dist * dist), 
          xcomp = (x - x2) / dist, 
          ycomp = (y - y2) / dist, 
          deltaV = force / 2;
      flake.velX -= deltaV * xcomp;
      flake.velY -= deltaV * ycomp;
    } else {
      flake.velX *= .98;
      if (flake.velY <= flake.speed) {
        flake.velY = flake.speed;
      }
      flake.velX += Math.cos(flake.step += .05) * flake.stepSize;
    }

    flake.y += flake.velY;
    flake.x += flake.velX;

    // 粒子超出画布时,按所属颜色配置重置
    if (flake.y >= canvas.height || flake.y <= 0 || flake.x <= 0) {
      resetFlake(flake);
    }

    ctx.fillStyle = flake.color;
    ctx.beginPath();
    ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
    ctx.fill();
  }
  requestAnimationFrame(flow);
};

// 修改重置逻辑:匹配粒子所属颜色的配置
function resetFlake(flake) {
  const config = colorConfigs[flake.colorKey];
  flake.x = config.startX;
  flake.y = config.startY;
  flake.speed = getRandomInRange(config.speedRange[0], config.speedRange[1]);
  flake.velY = flake.speed;
  flake.velX = config.velXMultiplier * canvas.width * getRandomInRange(0.8, 1.2);
  flake.stepSize = getRandomInRange(config.stepSizeRange[0], config.stepSizeRange[1]);
}

function init() {
  // 按比例分配不同颜色的粒子数量(这里各分一半,可按需调整)
  const redCount = Math.floor(flakeCount / 2);
  const blueCount = flakeCount - redCount;

  // 创建红色粒子组
  for (var i = 0; i < redCount; i++) {
    const config = colorConfigs.red;
    const speed = getRandomInRange(config.speedRange[0], config.speedRange[1]);
    particleArr.push({
      speed: speed,
      velY: speed,
      velX: config.velXMultiplier * canvas.width * getRandomInRange(0.8, 1.2),
      x: config.startX,
      y: config.startY,
      size: 6.692053245649504,
      stepSize: getRandomInRange(config.stepSizeRange[0], config.stepSizeRange[1]),
      step: 0,
      color: config.color,
      colorKey: "red" // 标记颜色归属,方便重置时匹配配置
    });
  }

  // 创建蓝色粒子组
  for (var i = 0; i < blueCount; i++) {
    const config = colorConfigs.blue;
    const speed = getRandomInRange(config.speedRange[0], config.speedRange[1]);
    particleArr.push({
      speed: speed,
      velY: speed,
      velX: config.velXMultiplier * canvas.width * getRandomInRange(0.8, 1.2),
      x: config.startX,
      y: config.startY,
      size: 6.692053245649504,
      stepSize: getRandomInRange(config.stepSizeRange[0], config.stepSizeRange[1]),
      step: 0,
      color: config.color,
      colorKey: "blue"
    });
  }

  requestAnimationFrame(flow);
};

canvas.addEventListener("mousemove", function(e) { 
  mouseX = e.clientX; 
  mouseY = e.clientY; 
});

window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  // 更新颜色配置的发射Y轴位置,适配新窗口尺寸
  colorConfigs.red.startY = canvas.height * 0.3;
  colorConfigs.blue.startY = canvas.height * 0.7;
  // 重置所有粒子到新的适配位置
  particleArr.forEach(flake => resetFlake(flake));
}

init(); 
</script>
</body>
</html>

关键修改说明

  1. 颜色配置系统:通过colorConfigs统一管理每种颜色的运动参数,让同色粒子的轨迹保持一致,自然形成独立动线。
  2. 粒子分组创建:初始化时不再随机分配颜色,而是按比例创建对应颜色的粒子,并标记colorKey,确保重置时能匹配到正确的配置。
  3. 窗口适配优化:窗口大小改变时,不仅调整画布尺寸,还同步更新发射位置并重置粒子,保证动线在任何窗口尺寸下都保持合理比例。
  4. 自然波动效果:用getRandomInRange给同色粒子的速度、步长添加细微差异,避免动线过于死板,保留粒子流动的自然感。

这样修改后,红色粒子会从画布右侧30%高度处向左下流动,蓝色粒子从右侧70%高度处向左下流动,各自形成清晰的独立动线,同时完全保留原有的鼠标碰撞避让效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 21:28:01