烟花效果实现html

canvas实现

<!DOCTYPE html>
<html>
  <head>
    <meta charset="gbk" />
    <title>烟花</title>
  </head>
  <body>
    <style type="text/css">
      body {
        background: #000;
        margin: 0;
      }

      canvas {
        cursor: crosshair;
        display: block;
      }
    </style>

    <canvas id="canvas">Canvas is not supported in your browser.</canvas>

    <script type="text/javascript">
      // 当在画布上做动画时,最好使用requestAnimationFrame而不是setTimeout或setInterval
      // 但不是所有浏览器都支持,有时需要前缀,所以我们需要一个shim
      window.requestAnimFrame = (function () {
        return (
          window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          function (callback) {
            window.setTimeout(callback, 1000 / 60);
          }
        );
      })();

      var canvas = document.getElementById("canvas"),
        ctx = canvas.getContext("2d"),
        // 画布的高度和宽度
        cw = window.innerWidth,
        ch = window.innerHeight,
        // 烟花容器
        fireworks = [],
        // 粒子容器
        particles = [],
        // 初始颜色
        hue = 120,
        // 默认烟花发射的个数阈值
        limiterTotal = 5,
        // 计数器
        limiterTick = 0,
        // 默认烟花发射的时间间隔
        timerTotal = 80,
        // 计时器
        timerTick = 0,
        mousedown = false,
        // 鼠标的x和y轴位置
        mx,
        my;

      canvas.width = cw;
      canvas.height = ch;

      function random(min, max) {
        return Math.random() * (max - min) + min;
      }

      // 计算两个点的直线距离
      function calculateDistance(p1x, p1y, p2x, p2y) {
        var xDistance = p1x - p2x,
          yDistance = p1y - p2y;
        return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
      }

      // 创建一个烟火
      function Firework(sx, sy, tx, ty) {
        // 烟花当前的位置
        this.x = sx;
        this.y = sy;
        // 烟花开始位置
        this.sx = sx;
        this.sy = sy;
        // 烟花结束位置
        this.tx = tx;
        this.ty = ty;

        // 烟花需要发射的距离
        this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
        // 烟花单次移动的距离
        this.distanceTraveled = 0;

        // 存储烟花发射过去的显示位置的数组
        this.coordinates = [];
        // 一次性显示的烟花路径上的的个数
        this.coordinateCount = 3;
        while (this.coordinateCount--) {
          this.coordinates.push([this.x, this.y]);
        }

        // 烟花的角度,速度,加速度,目标点的半径
        this.angle = Math.atan2(ty - sy, tx - sx);
        this.speed = 2;
        this.acceleration = 1.05;
        this.brightness = random(50, 70);
        this.targetRadius = 1;
      }

      // 更新动作
      Firework.prototype.update = function (index) {
        // 更新烟花位置
        this.coordinates.pop();
        this.coordinates.unshift([this.x, this.y]);

        // 烟花目标位置的圆设置放大效果
        if (this.targetRadius < 8) {
          this.targetRadius += 0.3;
        } else {
          this.targetRadius = 1;
        }

        //更新烟花的速度
        this.speed *= this.acceleration;

        //烟花移动的距离
        var vx = Math.cos(this.angle) * this.speed,
          vy = Math.sin(this.angle) * this.speed;
        this.distanceTraveled = calculateDistance(
          this.sx,
          this.sy,
          this.x + vx,
          this.y + vy
        );

        // 移动烟花 或 烟花爆开
        if (this.distanceTraveled >= this.distanceToTarget) {
          if (Math.random() < 0.3) {
            // 爆开两朵花
            createParticles(this.tx, this.ty, 30);
            setTimeout(() => {
              createParticles(this.tx, this.ty, 15);
            }, 600);
          } else {
            // 爆开一朵花(大小:20-40)
            createParticles(this.tx, this.ty, 30);
          }
          fireworks.splice(index, 1);
        } else {
          this.x += vx;
          this.y += vy;
        }
      };

      // 绘制烟花
      Firework.prototype.draw = function () {
        ctx.beginPath();
        ctx.moveTo(
          this.coordinates[this.coordinates.length - 1][0],
          this.coordinates[this.coordinates.length - 1][1]
        );
        ctx.lineTo(this.x, this.y);
        ctx.strokeStyle = "hsl(" + hue + ", 100%, " + this.brightness + "%)";
        ctx.stroke();

        // 绘制目标位置的圆点
        // ctx.beginPath();
        // ctx.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI * 2);
        // ctx.stroke();
      };

      // 爆开的烟火(一个粒子)
      function Particle(x, y) {
        this.x = x;
        this.y = y;
        // 爆开的粒子的路径
        this.coordinates = [];
        // 一次展示爆开粒子路径上点的个数
        this.coordinateCount = 5;
        while (this.coordinateCount--) {
          this.coordinates.push([this.x, this.y]);
        }
        //粒子发射角度,速度,减速度
        this.angle = random(0, Math.PI * 2);
        this.speed = random(1, 10);
        this.friction = 0.95;
        // 重力会把粒子往下拉
        this.gravity = 1;
        // 色调,亮度,透明度
        this.hue = random(hue - 20, hue + 20);
        this.brightness = random(50, 80);
        this.alpha = 1;
        // 设置粒子淡出的透明度
        this.decay = random(0.001, 0.05);
      }

      // 更新粒子位置
      Particle.prototype.update = function (index) {
        // 更新粒子
        this.coordinates.pop();
        this.coordinates.unshift([this.x, this.y]);

        // 更新粒子速度
        this.speed *= this.friction;

        // 更新粒子位置
        this.x += Math.cos(this.angle) * this.speed;
        this.y += Math.sin(this.angle) * this.speed + this.gravity;

        // 淡出
        this.alpha -= this.decay;
        if (this.alpha <= this.decay) {
          particles.splice(index, 1);
        }
      };

      // 绘制粒子
      Particle.prototype.draw = function () {
        ctx.beginPath();
        ctx.moveTo(
          this.coordinates[this.coordinates.length - 1][0],
          this.coordinates[this.coordinates.length - 1][1]
        );
        ctx.lineTo(this.x, this.y);
        ctx.strokeStyle =
          "hsla(" +
          this.hue +
          ", 100%, " +
          this.brightness +
          "%, " +
          this.alpha +
          ")";
        ctx.stroke();
      };

      // 创建粒子群
      function createParticles(x, y, num) {
        var particleCount = num;
        while (particleCount--) {
          particles.push(new Particle(x, y));
        }
      }

      // 发射烟花
      function loop() {
        // 这个函数将通过requestAnimationFrame无休止地运行
        requestAnimFrame(loop);

        hue += 0.5;

        // 通常,clearRect()将用于清除画布
        // 我们想要创造一个尾随效果
        // 将复合操作设置为destination-out将允许我们以特定的不透明度清除画布,而不是完全清除它
        ctx.globalCompositeOperation = "destination-out";
        ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
        ctx.fillRect(0, 0, cw, ch);

        // lighter创造明亮的亮点,因为烟花和粒子相互重叠
        ctx.globalCompositeOperation = "lighter";

        var i = fireworks.length;
        while (i--) {
          fireworks[i].draw();
          fireworks[i].update(i);
        }

        var i = particles.length;
        while (i--) {
          particles[i].draw();
          particles[i].update(i);
        }

        //随机烟花, timerTotal是烟花发送的时间间隔,timerTick是计数器
        if (timerTick >= timerTotal) {
          if (!mousedown) {
            fireworks.push(
              // 屏幕的左上角为坐标原点
              new Firework(cw / 2, ch, random(0, cw), random(0, ch / 2))
            );
            timerTick = 0;
          }
        } else {
          timerTick++;
        }

        // 鼠标点击烟花, limiterTotal是烟花发射的阈值, 当超过该阈值可以发射烟花
        if (limiterTick >= limiterTotal) {
          if (mousedown) {
            fireworks.push(new Firework(cw / 2, ch, mx, my));
            limiterTick = 0;
          }
        } else {
          limiterTick++;
        }
      }

      // 获取鼠标位置
      canvas.addEventListener("mousemove", function (e) {
        mx = e.pageX - canvas.offsetLeft;
        my = e.pageY - canvas.offsetTop;
      });

      canvas.addEventListener("mousedown", function (e) {
        e.preventDefault();
        mousedown = true;
      });

      canvas.addEventListener("mouseup", function (e) {
        e.preventDefault();
        mousedown = false;
      });

      window.onload = loop;
    </script>
  </body>
</html>



效果:
在这里插入图片描述

编写canvas动画的大致规律

  • 定义一个实体对象,包含需要绘制对象的基本信息, 一般包:对象位置(x,y)、对象方向(angle)、对象速度(speed)以及一些其他的对象属性
  • 准备一个对象数组,用来包含定义好的实体对象数组
  • 定义一个绘制函数,用来绘目标对象图形,一般根对象的位置属性来确定绘制的位置,再结合canvas绘制出对应的图形
  • 定义一个更新函数,用来更新目标对象的位置和形状,一般根据对象的速度和方向计算出对象水平和竖直方向移动的距离,更新位置,并确定对象的消失条件
  • 对象创建事件:实体对象需要一定的时机进行创建,一般是触发某个事件,鼠标移动或点击事件,创建的实体对象需要存储在定义的对象数组
  • canvas动画绘制函数:一切准备好之后就可以绘制动画效果了。一般定义一个定时器 setInterval、setTimeout(结合自调用) 或者 window.requestAnimationFrame 来实现动态效果。每次处理时,先调用canvas的函数清除上一"帧"的内容,再遍历对象数组调用绘制函数将对象数组中的内容绘制出来,同时调用更新函数更新对象的位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值