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

如何用原生JS实现流畅的粒子运动悬浮效果?

问题

需要实现一个页面粒子系统,满足以下需求:

  • 根据屏幕尺寸生成适量粒子,粒子始终悬浮在页面上方且保持在屏幕范围内
  • 粒子使用预定义的随机图片,尽量不需要额外HTML代码
  • 实现流畅的随机方向运动,避免当前固定间隔调整坐标导致的生硬瞬移效果
  • 不预设粒子运动方向,也不限制粒子仅在屏幕边缘反弹

解决方案核心优化点

  • 用requestAnimationFrame替代setInterval:利用浏览器原生刷新机制,让动画帧率与屏幕刷新率同步,彻底解决卡顿瞬移问题
  • 基于速度向量的运动逻辑:为每个粒子赋予持续的随机速度值,每一帧根据时间差计算位置变化,实现自然平滑的运动轨迹
  • 动态适配的粒子数量:根据屏幕面积和粒子大小自动计算合适数量,确保在不同设备上显示效果一致
  • 随机图片分配:预定义图片列表,生成粒子时随机选取,通过CSS遮罩实现图片显示
  • 无缝边界循环:粒子超出屏幕边缘后直接从对侧重新进入,替代生硬的反弹效果

完整代码示例

JavaScript

(() => {
  const PARTICLE_SIZE = 20; // 粒子大小
  const PARTICLE_SPEED_RANGE = [0.5, 2]; // 粒子速度范围(可调整)
  const PARTICLE_IMAGES = [
    './icons/square.svg',
    './icons/circle.svg',
    './icons/star.svg'
  ]; // 替换为你的图片路径

  let particles = [];
  let animationId = null;
  let lastFrameTime = Date.now();

  // 自动创建粒子容器(无需手动添加HTML)
  let container = document.getElementById('particles');
  if (!container) {
    container = document.createElement('div');
    container.id = 'particles';
    document.body.appendChild(container);
  }

  // 根据屏幕尺寸计算粒子数量
  const calculateParticleCount = () => {
    const screenArea = window.innerWidth * window.innerHeight;
    const densityFactor = 50; // 数值越大粒子越少,可调整密度
    const particleArea = PARTICLE_SIZE * PARTICLE_SIZE * densityFactor;
    return Math.floor(screenArea / particleArea);
  };

  // 创建单个粒子
  const createParticle = () => {
    const particle = document.createElement('span');
    
    // 随机初始位置
    particle.style.top = `${Math.random() * window.innerHeight}px`;
    particle.style.left = `${Math.random() * window.innerWidth}px`;
    
    // 随机分配图片(用CSS遮罩实现)
    const randomImg = PARTICLE_IMAGES[Math.floor(Math.random() * PARTICLE_IMAGES.length)];
    particle.style.maskImage = `url(${randomImg})`;
    particle.style.webkitMaskImage = `url(${randomImg})`; // 兼容webkit内核浏览器
    
    // 存储粒子的速度向量(x/y方向的随机速度)
    particle.speed = {
      x: (Math.random() - 0.5) * (PARTICLE_SPEED_RANGE[1] - PARTICLE_SPEED_RANGE[0]) + PARTICLE_SPEED_RANGE[0],
      y: (Math.random() - 0.5) * (PARTICLE_SPEED_RANGE[1] - PARTICLE_SPEED_RANGE[0]) + PARTICLE_SPEED_RANGE[0]
    };

    container.appendChild(particle);
    return particle;
  };

  // 生成所有粒子
  const generateParticles = () => {
    const count = calculateParticleCount();
    particles = Array.from({ length: count }, createParticle);
  };

  // 每一帧更新粒子位置
  const updateParticles = () => {
    const now = Date.now();
    const deltaTime = (now - lastFrameTime) / 16; // 以60fps为基准的时间补偿系数
    lastFrameTime = now;

    particles.forEach(particle => {
      let top = parseFloat(particle.style.top);
      let left = parseFloat(particle.style.left);

      // 根据速度和时间差更新位置
      top += particle.speed.y * deltaTime;
      left += particle.speed.x * deltaTime;

      // 边界循环处理:超出屏幕后从对侧重新出现
      if (top > window.innerHeight) top = -PARTICLE_SIZE;
      else if (top < -PARTICLE_SIZE) top = window.innerHeight;

      if (left > window.innerWidth) left = -PARTICLE_SIZE;
      else if (left < -PARTICLE_SIZE) left = window.innerWidth;

      particle.style.top = `${top}px`;
      particle.style.left = `${left}px`;
    });

    animationId = requestAnimationFrame(updateParticles);
  };

  // 窗口 resize 时重新生成粒子
  const handleResize = () => {
    cancelAnimationFrame(animationId);
    particles.forEach(p => p.remove());
    generateParticles();
    lastFrameTime = Date.now();
    updateParticles();
  };

  // 初始化启动
  generateParticles();
  updateParticles();
  window.addEventListener('resize', handleResize);
})();

CSS

#particles {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none; /* 确保粒子不干扰页面其他元素的点击交互 */
  opacity: 0.25;
}

#particles span {
  position: absolute;
  width: 20px;
  height: 20px;
  background-color: #333; /* 遮罩图片的基础颜色,可根据需求修改 */
  mask-repeat: no-repeat;
  mask-size: contain;
  mask-position: center;
  /* 兼容webkit内核浏览器 */
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  -webkit-mask-position: center;
}

HTML

<!-- 无需手动添加容器,JS会自动创建 -->

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 02:57:55