canvas mouse click appears fireworks effect

Main ideas:

  • The mouse click event triggers the dot to appear at the mouse

  • Point size, color, number random

  • There is a small white circle as a trajectory, constantly changing the radius to become larger, weaker and disappear.

  • The dot moves outward, becoming smaller and disappearing. With the distance of movement, the speed of movement changes.

What needs to be dealt with is that the X and Y axes of the dots can be determined by clicking on the mouse, but where to move and how to move is a problem.
So the target coordinate of the circle is determined first, then it moves from the original coordinate of the circle (i.e. the click of the mouse) to the target coordinate, and x,y are moved in a proportion to make it not unbalanced in the case of unequal moving distance.

Function codes for random generation of target coordinates of circular points:

let endpos = (x, y) => {
  let angle = random(0, 360) * Math.PI / 180,
    value = random(20, 150),
    radius = [-1, 1][random(0, 1)] * value;

  return {
    x: x + radius * Math.cos(angle),
    y: y + radius * Math.sin(angle)
  }
}

Because a large number of random numbers are needed, encapsulate a function to generate random numbers.

let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

The moving function of a circle, we need to move from the original coordinate to the target coordinate from fast and slow.
The generation ratio is divided into three segments. That is, the first coordinate to the first coordinate is the first segment, the first coordinate to the second coordinate is the second segment, and the second coordinate to the target coordinate is the third segment. If the current coordinate is in the first paragraph, the faster, and so on.

Now there are two cases, one is that the target coordinate is larger than the original coordinate, then the first section is the one closest to the original coordinate (Math. Max (current, f f) = current?(Math. Max (current, m m) = current?? s: m): f), the second case is that the target coordinate is smaller than the original coordinate, then the first section is the farthest from the original coordinate.

//According to the pace of moving forward in different distances, it can be divided into three stages. The one near the starting point is the fastest, the middle is the general, and the farthest is the slowest.
//Distinguishing the case where the target point is less than the starting point
//Ratio is the ratio of the distance between two points. The larger the ratio, the slower the walking is.
  moveFun(start, end, current) {
    let s = random(26, 35),
      m = random(20, 25),
      f = random(15, 20),
      ff = start.x + ~~((end.x - start.x) / 3),
      mm = ff + ~~((end.x - start.x) / 3),
      ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)),
      mp = {
        x: end.x - start.x,
        y: end.y - start.y
      };

    return {
      x: Math.abs(mp.x / ratio),
      y: Math.abs(mp.y / ratio)
    }
  }

Each dot acts as an individual, with its own moving function and moving target coordinates. Each Animation, the dots are redrawn to update the latest coordinates
Code for small dots:

class Circle {
  constructor(x, y) {
    this.r = random(9, 13)
    this.opos = {}
    this.x = this.opos.x = x
    this.y = this.opos.y = y

    this.colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C']
    this.color = this.colors[random(0, this.colors.length)];
    this.tpos = endpos(x, y)
  }

  creatCircle(ctx) {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
    ctx.closePath()
    ctx.fillStyle = this.color
    ctx.fill()
  }

  //According to the pace of moving forward in different distances, it can be divided into three stages. The one near the starting point is the fastest, the middle is the general, and the farthest is the slowest.
  //Distinguishing the case where the target point is less than the starting point
  //Ratio is the ratio of the distance between two points. The larger the ratio, the slower the walking is.
  moveFun(start, end, current) {
    let s = random(26, 35),
      m = random(20, 25),
      f = random(15, 20),
      ff = start.x + ~~((end.x - start.x) / 3),
      mm = ff + ~~((end.x - start.x) / 3),
      ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)),
      mp = {
        x: end.x - start.x,
        y: end.y - start.y
      };

    return {
      x: Math.abs(mp.x / ratio),
      y: Math.abs(mp.y / ratio)
    }
  }

  //Move according to the calculated moving value
  //If the target coordinate is larger than the original coordinate, it should move to the right, the maximum should not exceed the target coordinate, and vice versa, the minimum should not be less than the target coordinate.
  move() {
    var movepos = this.moveFun(this.opos, this.tpos, this.x);

    this.x = (this.opos.x > this.tpos.x) ? Math.max(this.x - movepos.x, this.tpos.x) : Math.min(this.x + movepos.x, this.tpos.x)
    this.y = this.opos.y > this.tpos.y ? Math.max(this.y - movepos.y, this.tpos.y) : Math.min(this.y + movepos.y, this.tpos.y)
    this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0)

  }
}

Big circle is a circle created from the mouse click, and constantly changes its radius to expand outward, and become more and more transparent. When it is already large and transparent, it means that our animation is over, so take this as the standard, stop animation, and empty the screen.
Big circle code:

class BigCircle {
  constructor(x, y) {
    this.bR = random(16, 32)
    this.overR = random(60, 100)
    this.x = x
    this.y = y
    this.op = 1
  }

  creatBigCircle(ctx) {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI)
    ctx.closePath()
    ctx.strokeStyle = 'rgba(128, 128, 128, ' + this.op + ')'
    ctx.stroke()
  }

  changeR() {
    this.bR = Math.min(this.bR += random(1, 4), this.overR);
    this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0)
  }

  //Check whether the screen is running and empty the screen according to Big Circle
  complete() {
    return this.bR >= this.overR && this.op <= 0;
  }
}

All code:

//canvas mouse click fireworks special effect

let endpos = (x, y) => {
  let angle = random(0, 360) * Math.PI / 180,
    value = random(20, 150),
    radius = [-1, 1][random(0, 1)] * value;

  return {
    x: x + radius * Math.cos(angle),
    y: y + radius * Math.sin(angle)
  }
}

let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

class Circle {
  constructor(x, y) {
    this.r = random(9, 13)
    this.opos = {}
    this.x = this.opos.x = x
    this.y = this.opos.y = y

    this.colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C']
    this.color = this.colors[random(0, this.colors.length)];
    this.tpos = endpos(x, y)
  }

  creatCircle(ctx) {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
    ctx.closePath()
    ctx.fillStyle = this.color
    ctx.fill()
  }

  //According to the pace of moving forward in different distances, it can be divided into three stages. The one near the starting point is the fastest, the middle is the general, and the farthest is the slowest.
  //Distinguishing the case where the target point is less than the starting point
  //Ratio is the ratio of the distance between two points. The larger the ratio, the slower the walking is.
  moveFun(start, end, current) {
      let s = random(26, 35),
        m = random(20, 25),
        f = random(15, 20),
        ff = start.x + ~~((end.x - start.x) / 3),
        mm = ff + ~~((end.x - start.x) / 3),
        ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)),
        mp = {
          x: end.x - start.x,
          y: end.y - start.y
        };

      return {
        x: Math.abs(mp.x / ratio),
        y: Math.abs(mp.y / ratio)
      }
    }
    
    //Move according to the calculated moving value
    //If the target coordinate is larger than the original coordinate, it should move to the right, the maximum should not exceed the target coordinate, and vice versa, the minimum should not be less than the target coordinate.
  move() {
    var movepos = this.moveFun(this.opos, this.tpos, this.x);

    this.x = (this.opos.x > this.tpos.x) ? Math.max(this.x - movepos.x, this.tpos.x) : Math.min(this.x + movepos.x, this.tpos.x)
    this.y = this.opos.y > this.tpos.y ? Math.max(this.y - movepos.y, this.tpos.y) : Math.min(this.y + movepos.y, this.tpos.y)
    this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0)

  }
}

class BigCircle {
  constructor(x, y) {
    this.bR = random(16, 32)
    this.overR = random(60, 100)
    this.x = x
    this.y = y
    this.op = 1
  }

  creatBigCircle(ctx) {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI)
    ctx.closePath()
    ctx.strokeStyle = 'rgba(128, 128, 128, ' + this.op + ')'
    ctx.stroke()
  }

  changeR() {
    this.bR = Math.min(this.bR += random(1, 4), this.overR);
    this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0)
  }

  //Check whether the screen is running and empty the screen according to Big Circle
  complete() {
    return this.bR >= this.overR && this.op <= 0;
  }
}

window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

window.clearRequestTimeout = window.cancelAnimationFrame || window.mozCancelRequestAnimationFrame || window.webkitCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame;


let c = document.getElementById("fireworks"),
  w = c.width = c.offsetWidth,
  h = c.height = c.offsetHeight,

  ctx = c.getContext("2d"),
  nums = 40,
  circles = [],
  bCircle = null,
  animationId = false;

let int = function(x, y) {
  circles = []

  if (animationId) clearRequestTimeout(animationId)

  for (let i = nums; i-- > 0;) {
    circles.push(new Circle(x, y))
  }

  bCircle = new BigCircle(x, y)
  creat()
}

let creat = function() {
  ctx.clearRect(0, 0, w, h);

  circles.forEach(function(v) {
    v.move();
    v.creatCircle(ctx)
  })

  bCircle.changeR()
  bCircle.creatBigCircle(ctx)

  animationId = requestAnimationFrame(creat)

  if (bCircle.complete()) {
    //Take big circle as standard, empty screen and stop animation
    ctx.clearRect(0, 0, w, h);
    clearRequestTimeout(animationId)
  }
}

c.onclick = function(e) {
  e = e || window.event;
  int(e.clientX, e.clientY)
}

html is

<canvas id="fireworks" width="500px" height="500px"></canvas>

Keywords: Javascript less

Added by ryochiji on Mon, 24 Jun 2019 23:37:52 +0300