JS:透明背景烟花效果适配移动端

 js透明背景烟花效果并适配移动端,附带详细注释。

效果:

 Js及HTML:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>透明背景烟花效果适配移动端</title>
  <style>
    body, html{margin:0;padding:0;overflow:hidden;}
    .topic {position:relative;width:100%;height:50vh;background:url(https://blog.csdn.net/xxx.jpg) no-repeat top left;background-size:100% 100%;}
    #fireworks{position:absolute;top:0;left:0;width:100%;height:100%;}
  </style>
</head>
<body>
  <div class="topic">
    <canvas id="fireworks"></canvas>
  </div>
<script>
// 烟花效果
const canvas = document.getElementById('fireworks');
const ctx = canvas.getContext('2d');

// 响应式参数
let isSmallScreen = window.innerWidth < 860;

// 设置画布尺寸,使其与父容器.topic的尺寸保持一致 并在窗口大小变化时调整已有粒子的位置
function resizeCanvas() {
  // 获取指定父级 topic 元素
  const topicElement = document.querySelector('.topic')
  // 设置 canvas 尺寸
  canvas.width = topicElement.clientWidth
  canvas.height = topicElement.clientHeight

  // 检测屏幕尺寸变化
  const newIsSmallScreen = window.innerWidth < 860;
  if (newIsSmallScreen !== isSmallScreen) {
    isSmallScreen = newIsSmallScreen;
    updateFireworksDensity();
  }
}

// 根据屏幕尺寸更新烟花发射密度和粒子数量 小屏幕设备减少烟花密度和粒子数量以优化性能
function updateFireworksDensity() {
  if (!fireworksManager) return;

  // 清除现有定时器
  clearInterval(fireworksManager.autoLaunchInterval);

  // 根据屏幕尺寸设置不同的发射间隔和粒子数量
  if (isSmallScreen) {
    // 小屏幕:减少发射频率和粒子数量
    fireworksManager.autoLaunchInterval = setInterval(() => {
      fireworksManager.launchFirework();
    }, 2500 + Math.random() * 5000); // 每2.5-5秒发射一个烟花

    // 修改爆炸函数,减少粒子数量
    fireworksManager.explode = (x, y, color) => {
      for (let i = 0; i < 60 + Math.random() * 60; i++) {
        fireworksManager.particles.push(new Particle(x, y, color));
      }
    }
  } else {
    // 大屏幕:恢复原始设置
    fireworksManager.autoLaunchInterval = setInterval(() => {
      fireworksManager.launchFirework();
    }, 100 + Math.random() * 1500); // 每0.1-2.3秒发射一个烟花

    // 恢复原始爆炸函数
    fireworksManager.explode = (x, y, color) => {
      for (let i = 0; i < 120 + Math.random() * 120; i++) {
        fireworksManager.particles.push(new Particle(x, y, color));
      }
    }
  }
}

// 烟花粒子类表示单个粒子(火箭或爆炸碎片)
class Particle {
  constructor(x, y, color, isRocket = false) {
    this.x = x; // 粒子的x坐标(初始位置)
    this.y = y; // 粒子的y坐标(初始位置)
    this.color = color; // 粒子颜色(HSL格式,如'hsl(120, 100%, 70%)')
    this.isRocket = isRocket; // 是否为火箭(true: 火箭,false: 爆炸碎片)
    this.opacity = 1; // 粒子透明度(初始为完全不透明)
    this.age = 0; // 粒子生命周期计时器(帧计数)
    this.lifeSpan = 80 + Math.random() * 40; // 粒子总寿命(80~120帧)

    if (this.isRocket) {
      // 火箭参数
      this.velocity = {
        x: (Math.random() - 0.5) * 2, // 水平速度(-1~1之间随机值,控制横向偏移)
        y: Math.random() * -6 - 10 // 垂直速度(-16~-10之间随机值,负值表示向上发射)
      };
      this.gravity = 0.07; // 重力加速度(控制火箭上升高度和下落速度)
      this.friction = 0.99; // 摩擦系数(速度衰减因子,值越小减速越快)
      this.minExplosionHeight = 0.1; // 最小爆炸高度(相对于画布高度的比例)
      this.maxExplosionHeight = 0.9; // 最大爆炸高度(相对于画布高度的比例)
    } else {
      // 爆炸粒子参数 - 线条效果
      const angle = Math.random() * Math.PI * 2; // 随机角度(0~360度)
      const speed = Math.random() * 8 + 5; // 随机速度(5~13之间,控制爆炸扩散范围)
      this.velocity = {
        x: Math.cos(angle) * speed, // 水平速度分量
        y: Math.sin(angle) * speed // 垂直速度分量
      };
      this.gravity = 0.03; // 减小重力,使粒子飞更远
      this.friction = 0.96; // 增加摩擦,使速度衰减更慢
      this.size = Math.random() * 2 + 1; // 线条粗细(1~3之间随机值)
      this.length = Math.random() * 15 + 10; // 线条长度(10~25之间随机值,决定爆炸视觉效果)
      this.lastX = x; // 记录上一帧位置(用于绘制线条)
      this.lastY = y; // 记录上一帧位置(用于绘制线条)
    }
  }

 //更新粒子状态(位置、速度、生命周期等)
  update() {
    this.age++;

    if (this.isRocket) {
      // 火箭更新
      this.velocity.x *= this.friction; // 水平速度衰减
      this.velocity.y *= this.friction; // 垂直速度衰减
      this.velocity.y += this.gravity; // 应用重力
      this.x += this.velocity.x; // 更新x位置
      this.y += this.velocity.y; // 更新y位置
    } else {
      // 爆炸粒子更新 - 线条效果
      this.lastX = this.x; // 保存上一帧位置
      this.lastY = this.y; // 保存上一帧位置

      this.velocity.x *= this.friction; // 水平速度衰减
      this.velocity.y *= this.friction; // 垂直速度衰减
      this.velocity.y += this.gravity; // 应用重力
      this.x += this.velocity.x; // 更新x位置
      this.y += this.velocity.y; // 更新y位置

      // 随时间减少不透明度,实现淡出效果
      this.opacity = 1 - (this.age / this.lifeSpan);
      if (this.opacity < 0) this.opacity = 0;
    }
  }

  // 绘制粒子(火箭或爆炸碎片)
  draw() {
    ctx.save();
    ctx.globalAlpha = this.opacity; // 设置透明度

    if (this.isRocket) {
      // 绘制火箭(三角形形状)
      ctx.beginPath();
      ctx.moveTo(this.x, this.y + 5);
      ctx.lineTo(this.x + 2, this.y - 5);
      ctx.lineTo(this.x - 2, this.y - 5);
      ctx.closePath();
      ctx.fillStyle = this.color;
      ctx.fill();

      // 绘制火箭尾焰
      ctx.beginPath();
      ctx.moveTo(this.x, this.y + 5);
      ctx.lineTo(this.x + 1, this.y + 10);
      ctx.lineTo(this.x - 1, this.y + 10);
      ctx.closePath();
      ctx.fillStyle = 'rgba(255, 200, 0, 0.8)';
      ctx.fill();
    } else {
      // 绘制线条形状的爆炸粒子
      ctx.beginPath();
      ctx.moveTo(this.lastX, this.lastY); // 线条起点(上一帧位置)
      ctx.lineTo(this.x, this.y); // 线条终点(当前位置)
      ctx.lineWidth = this.size; // 设置线条粗细
      ctx.strokeStyle = this.color; // 设置线条颜色
      ctx.stroke(); // 绘制线条

      // 线条末端添加一个小点,增强视觉效果
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.size / 20, 0, Math.PI * 2);
      ctx.fillStyle = this.color;
      ctx.fill();
    }

    ctx.restore();
  }
}

// 烟花管理器 统筹管理所有粒子的生命周期和动画循环
class FireworksManager {
  constructor() {
    this.particles = []; // 存储所有粒子的数组
    this.autoLaunchInterval = null; // 自动发射定时器
  }

  // 生成随机颜色
  getRandomColor() {
    return `hsl(${Math.floor(Math.random() * 360)}, 100%, 70%)`;
  }

 //  发射烟花火箭
  launchFirework(x = Math.random() * canvas.width, y = canvas.height) {
    this.particles.push(new Particle(x, y, this.getRandomColor(), true));
  }

  // 触发爆炸效果
  explode(x, y, color) {
    const count = isSmallScreen ? 60 + Math.random() * 60 : 120 + Math.random() * 120;
    for (let i = 0; i < count; i++) {
      this.particles.push(new Particle(x, y, color));
    }
  }

  //启动自动发射烟花
  startAutoLaunch() {
    updateFireworksDensity()
  }

  //更新所有粒子状态,处理边界检测和爆炸条件
  update() {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      const particle = this.particles[i];
      particle.update(); // 更新粒子状态

      // 确保粒子不会超出画布范围
      if (particle.y > canvas.height) {
        particle.y = canvas.height;
        // 如果是火箭,到达底部后爆炸
        if (particle.isRocket) {
          this.explode(particle.x, particle.y, particle.color);
          this.particles.splice(i, 1); // 移除火箭粒子
        }
      }

      // 修改火箭爆炸条件
      if (particle.isRocket) {
        // 检查火箭是否在Canvas高度范围内且开始下落
        const inRange = particle.y < canvas.height * particle.maxExplosionHeight && particle.y > canvas.height * particle.minExplosionHeight;

        // 如果火箭超出Canvas顶部,强制在顶部爆炸
        if ((inRange && particle.velocity.y >= 0) || particle.y < 0) {
          // 火箭达到最高点并开始下落时爆炸
          this.explode(particle.x, particle.y, particle.color);
          this.particles.splice(i, 1); // 移除火箭粒子
        }
      }

      // 移除生命周期结束的粒子(非火箭且透明度为0)
      if (!particle.isRocket && particle.opacity <= 0) {
        this.particles.splice(i, 1);
      }
    }
  }

  // 绘制所有粒子
  draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
    for (const particle of this.particles) {
      particle.draw(); // 绘制每个粒子
    }
  }

  // 循环
  animate() {
    this.update();
    this.draw();
    requestAnimationFrame(this.animate.bind(this));
  }

  // 启动烟花效果
  start() {
    this.animate(); // 启动动画循环
    this.startAutoLaunch(); // 启动自动发射
  }
}

// 初始化烟花管理器
const fireworksManager = new FireworksManager();

// 设置画布尺寸并监听窗口大小变化
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 播放烟花
fireworksManager.start();

// 点击事件 - 点击发射烟花
canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  fireworksManager.launchFirework(
    e.clientX - rect.left,
    e.clientY - rect.top
  )
})
</script>
</body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值