HTML

Don’t need too many elements, simple is true

<div class="debut">
  <! -- Background -->
  <canvas class="music-cover-background" id="background">your brower does not support canvas</canvas>
  
  <! -- Foreground section -->
  <div class="music-cover">
    <img src="images/1753378458.jpg" class="music-cover-image"></img>
  </div>
</div>
Copy the code





CSS

Find an opening and draw a circle

.debut {
  position: absolute;
  width: 100%;
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.music-cover {
  width: 23.75 rem;  /* 380px */
  height: 23.75 rem; /* 380px */
  box-sizing: border-box;
  border:.125rem solid #B3B3B3; /* 2px solid #B3B3B3 */
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
Copy the code

Center the layout using Flex


Put a picture in the circle and make it go around

.music-cover-image {
  width: 21.25 rem; /* 340px */
  height: 21.25 rem; /* 340px */
  border: none;
  border-radius: 50%;
  animation: rotate infinite linear 25s;
}

@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }}Copy the code

Animation time 25s was measured by me with a stopwatch


Draw a shadow behind the picture and choose a simple color

.music-cover::before {
  content: ""; position: absolute; Width: 21.25 rem; /* 340px */ height: 21.25rem; /* 340px */ border-radius: 50%; The filter: the blur (1.875 rem); /* 30px */ background-image: radial-gradient(white, silver); }Copy the code

Pseudo-elements are enough






canvas

Let’s place the canvas

.music-cover-background {
  position: absolute;
}
Copy the code

Position: Absolute centered because the parent element. Debut uses flex layout


Right after the size

const canvas = document.getElementById('background');
canvas.width = canvas.height = Math.ceil(canvas.parentNode.firstElementChild.offsetWidth * 1.68421);
Copy the code


Defining a triangle

const PI2 = 2 * Math.PI;
class Triangle {
  constructor(context, speed, pole, range) {
    this.ctx = context;
    this.pole = pole;
    this.range = range;
    this.speed = speed;
    this.points = [[0.0], [0.0], [0.0]].this.__restart();
  }

  __restart() {
    this.angle = Math.random() * PI2; // Generate a random movement direction
    this.speedX = Math.cos(this.angle) * this.speed;
    this.speedY = Math.sin(this.angle) * this.speed;
    this.opacity = 1;

    const dist = Math.random() * 150; // In order to generate a well-proportioned triangle, let the triangle start at a random distance dist from the pole
    const distX = Math.cos(this.angle) * dist;
    const distY = Math.sin(this.angle) * dist;
    constTheta equalsMath.random() * PI2; // Rotate the triangle by a random θ degree
    const x2 = Math.random() * 10;
    const y2 = 20 + Math.random() * 20;
    const x3 = 10 + Math.random() * 15;
    const y3 = 12 + Math.random() * 6;
    this.points[0] [0] = Math.floor(this.pole[0] + distX);
    this.points[0] [1] = Math.floor(this.pole[1] + distY);
    this.points[1] [0] = Math.floor(this.pole[0] + distX + (x2 * MathCos (theta) - y2 *MathSin (theta)));this.points[1] [1] = Math.floor(this.pole[1] + distY + (y2 * MathCos (theta) + x2 *MathSin (theta)));this.points[2] [0] = Math.floor(this.pole[0] + distX + (x3 * MathCos (theta) - y3 *MathSin (theta)));this.points[2] [1] = Math.floor(this.pole[1] + distY + (y3 * MathCos (theta) + x3 *MathSin (theta))); } __distance() {const dx = this.points[0] [0] - this.pole[0];
    const dy = this.points[0] [1] - this.pole[1];
    return Math.floor(Math.sqrt(dx * dx + dy * dy));
  }

  __lerp(src, dst, coeff) {
    return src + (dst - src) * coeff;
  }

  __update() {
    const dist = this.__distance();
    if (dist - this.range > 0.0001)
      this.__restart();
    else {
      this.points.forEach((point, index) = > {
        this.points[index][0] = point[0] + this.speedX;
        this.points[index][1] = point[1] + this.speedY;
      });
      this.opacity = this.__lerp(1.0, dist / this.range);
    }
  }

  render() {
    this.__update();
    this.ctx.lineWidth = 2;
    this.ctx.lineJoin = "miter";
    this.ctx.strokeStyle = `rgba(179, 179, 179, The ${this.opacity}) `;
    this.ctx.beginPath();
    this.ctx.moveTo(this.points[0] [0].this.points[0] [1]);
    this.ctx.lineTo(this.points[1] [0].this.points[1] [1]);
    this.ctx.lineTo(this.points[2] [0].this.points[2] [1]);
    this.ctx.closePath();
    this.ctx.stroke();
    this.ctx.fillStyle = 'rgba(67, 67, 67, .2)';
    this.ctx.fill(); }}Copy the code


Define a “scene”

class Scene {
  constructor(canvas) {
    this.cvs = canvas;
    this.ctx = canvas.getContext('2d');
    this.triangleSet = [];
    this.triangleNum = 25; // Number of triangles
    const realm = this.cvs.width / 2; // Canvas center
    for (let i = 0; i < this.triangleNum; ++i)
      this.triangleSet[i] = new Triangle(this.ctx, 1.5, [realm, realm], realm);
  }

  render() {
    this.ctx.clearRect(0.0.this.cvs.width, this.cvs.height);  // Clear the canvas in time
    this.triangleSet.forEach(triangle= > triangle.render());
  }

  run() {
    if (!this.timer) {
      this.timer = setInterval(this.render.bind(this), 25);
    }
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0; }}}Copy the code


Finally, “Run scene”

const canvas = document.getElementById('background');
canvas.width = canvas.height = Math.ceil(canvas.parentNode.lastElementChild.offsetWidth * 1.68421);
const scene = new Scene(canvas);
scene.run();
Copy the code