canvas -页面背景泡沫效果代码解读

342 阅读1分钟

1.先看效果

在这里插入图片描述

2.源码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <canvas id="canvas"></canvas>
</body>
<style>
  body {
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAI0lEQVQIW2NkwAT/GdHE/gP5jMiCYAGQIpggXAAmiCIAEgQAAE4FBbECyZcAAAAASUVORK5CYII=) #222;
  }
</style>

<script>
  class Particles {
    constructor() {
      // 泡泡的颜色列表
      this.colors = ['255, 255, 255', '255, 99, 71', '19, 19, 19'];
      // 泡泡是否需要渐变,这里都是要渐变的
      this.blurry = true;
      //adds white border
      this.border = false;
      // 泡泡的半径范围
      this.minRadius = 10;
      this.maxRadius = 35;
      // 泡泡的透明度范围
      this.minOpacity = 0.005;
      this.maxOpacity = 0.5;
      //particle speed min/max
      this.minSpeed = 0.05;
      this.maxSpeed = 0.5;
      //frames per second
      this.fps = 60;
      // 页面中泡泡的数量
      this.numParticles = 175;
      //required canvas variables
      this.canvas = document.getElementById('canvas');
      this.ctx = this.canvas.getContext('2d');
    }

    init() {
      this.render();
      this.createCircle();
    }

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

    // canvas基本配置
    render() {
      var self = this;
      var wHeight = window.innerHeight;
      var wWidth = window.innerWidth;

      self.canvas.width = wWidth;
      self.canvas.height = wHeight;

      window.addEventListener('resize', () => self.render);
    };

    // 创建泡泡
    createCircle() {
      var particle = [];

      for (var i = 0; i < this.numParticles; i++) {
        var self = this;
        // ~~ 是将某一数据转换成数字类型的写法,类似!!的转换成Boolean类型的用法
        // 所以这里是取颜色列表里的某个随机颜色
        var color = self.colors[~~self._rand(0, self.colors.length)];

        // 保存所有的泡泡
        particle[i] = {
          radius: self._rand(self.minRadius, self.maxRadius), // 半径
          xPos: self._rand(0, canvas.width), // 初始x轴位置
          yPos: self._rand(0, canvas.height), // 初始y轴位置
          xVelocity: self._rand(self.minSpeed, self.maxSpeed), // x轴运行速度
          yVelocity: self._rand(self.minSpeed, self.maxSpeed), // y轴运行速度
          color: `rgba(${color},${self._rand(self.minOpacity, self.maxOpacity)})` // 随机的颜色+随机的透明度
        };

        self.draw(particle, i);
      }
      // 所有的泡泡绘制结束以后,开始执行动画
      self.animate(particle, this);
    };

    draw(particle, i) {
      var self = this;
      var ctx = self.ctx;

      if (self.blurry === true) {
        // 泡泡的渐变设置
        var grd = ctx.createRadialGradient(
          particle[i].xPos,
          particle[i].yPos,
          particle[i].radius, // 里面的圆。对应 1
          particle[i].xPos,
          particle[i].yPos,
          particle[i].radius / 1.5 // 外面的圆。对应 0
        );

        grd.addColorStop(0.0, 'rgba(34, 34, 34, 0)'); // 0:外面圆的最终渐变色
        grd.addColorStop(1.0, particle[i].color); // 1:中心圆的渐变色
        ctx.fillStyle = grd;
      } else {
        // 根本不会走到这一步
        ctx.fillStyle = particle[i].color;
      }

      if (self.border === true) {
        ctx.strokeStyle = '#fff';
        ctx.stroke();
      }

      ctx.beginPath();
      // 画出泡泡
      ctx.arc(
        particle[i].xPos,
        particle[i].yPos,
        particle[i].radius,
        0,
        2 * Math.PI,
        false
      );
      ctx.fill();
    };

    animate(particle, self) {
      // var self = this;
      var ctx = self.ctx;
      self.clearCanvas();

      setInterval(function () {
        self.clearCanvas();
        for (var i = 0; i < self.numParticles; i++) {

          // 增加x,y轴的偏移量
          particle[i].xPos += particle[i].xVelocity;
          particle[i].yPos -= particle[i].yVelocity;

          // 防止泡泡抛出屏幕
          if (
            particle[i].xPos > self.canvas.width + particle[i].radius ||
            particle[i].yPos > self.canvas.height + particle[i].radius
          ) {
            self.resetParticle(particle, i);
          } else {
            self.draw(particle, i);
          }
        }
      }, 1000 / self.fps);

    };

    resetParticle(particle, i) {
      var self = this;

      var random = self._rand(0, 1);

      if (random > 0.5) {
        // 从左边出来
        particle[i].xPos = -particle[i].radius; 
        particle[i].yPos = self._rand(0, canvas.height);
      } else {
        // 从底部出来
        particle[i].xPos = self._rand(0, canvas.width);
        particle[i].yPos = canvas.height + particle[i].radius;
      }
      self.draw(particle, i);
    };

    clearCanvas() {
      this.ctx.clearRect(0, 0, canvas.width, canvas.height);
    };

  }
  // 初始化
  var particle = new Particles();
  particle.init();
</script>

</html>

3.实现思路

这个实现思路比较简单:

  • 就是一个泡泡一个泡泡的绘制,每个泡泡有一个初始位置,以及初始运动方向
  • 通过循环定时器增加x,y轴的偏移量,然后重新绘制这个泡泡
  • 当泡泡到达屏幕边缘时随机重置此泡泡的初始位置,然后继续绘制这个泡泡

其中处理泡泡的边缘渐变的是createRadialGradient方法,前面三个参数的圆对应addColorStop第一个参数为1时的渐变色;后面三个参数的圆对应第1个参数为0的渐变色。