Summary of realizing mouse tracking animation with canvas

Posted by RobertSubnet on Thu, 06 Jan 2022 03:00:35 +0100

Summary of canvas mouse tracking animation ideas

effect

Source code

html

Click to view the code
<!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>Mouse tracking animation</title>
    <style>
      canvas {
        margin: 50px auto;
        display: block;
        box-shadow: 0 0 10px rgb(0 0 0 / 50%);
      }
    </style>
  </head>
  <body onload="draw()">
    <canvas width="800" height="500"></canvas>

    <script src="./js/Mouse tracking animation.js"></script>
    <script>
      let offsetX = 0
      let offsetY = 0
      let targetArr = []
      function draw() {
        const canvas = document.querySelector('canvas')
        const ctx = canvas.getContext('2d')
        canvas.onmousemove = function (e) {
          offsetX = e.offsetX
          offsetY = e.offsetY
        }
        targetArr = prepareLine(canvas.width / 2, canvas.height / 2)
        startAnimation(ctx, canvas.width, canvas.height)
      }
      function prepareLine(startX, startY) {
        const res = []
        offsetX = startX
        offsetY = startY
        for (let i = 0; i < 100; i++) {
          res.push(new Line(startX, startY))
        }
        return res
      }

      function startAnimation(ctx, width, height) {
        requestAnimationFrame(() => startAnimation(ctx, width, height))
        // Transparency controls the length of the long tail effect
        ctx.fillStyle = 'rgba(0,0,0,0.05)'
        ctx.fillRect(0, 0, width, height)
        targetArr.forEach((obj) => {
          obj.draw(ctx, offsetX, offsetY)
        })
      }
    </script>
  </body>
</html>

js

Mouse tracking animation js

Click to view the code
class Line {
  constructor(startX, startY) {
    this.startX = startX

    this.startY = startY
    // Random angle of line
    this.randomAngle = this._getRandomAngle()
    // Control range
    this.maxLineLength = 150
    // Control thickness
    this.lineWidth = 4
    // Frequency of angle change (speed)
    this.angleRate = 0.02
    // From maxLineLength, control range
    this.lineLength = this._getRandomLength()
    this.lineColor = this._getRandomColor()
  }

  setMaxLineLength(length) {
    this.maxLineLength = length
  }

  setLineWidth(width) {
    this.lineWidth = width
  }

  draw(ctx, offsetX, offsetY) {
    ctx.save()
    const { lineW, lineH } = this._computeLocationData()
    ctx.lineWidth = this.lineWidth
    ctx.strokeStyle = this.lineColor
    ctx.beginPath()
    // Location of the previous point
    ctx.moveTo(this.startX, this.startY)
    // x coordinate from mouse position
    this.startX = offsetX + lineW
    // y coordinate from mouse position
    this.startY = offsetY + lineH
    // Draw from the position of the previous point to the position of the next point (this.angleRate degree difference in angle)
    ctx.lineTo(this.startX, this.startY)
    ctx.stroke()
    ctx.closePath()
    ctx.restore()
  }

  _computeLocationData() {
    this.randomAngle += this.angleRate
    return {
      lineW: Math.cos(this.randomAngle) * this.lineLength, // Random length of line (x coordinate)
      lineH: Math.sin(this.randomAngle) * this.lineLength // Random height of the line (y coordinate)
    }
  }

  _getRandomAngle() {
    return Math.random() * Math.PI * 2
  }

  _getRandomLength() {
    return Math.random() * this.maxLineLength
  }

  _getRandomColor() {
    const s = '0123456789ABCDEF'
    let c = '#'
    for (let i = 0; i < 6; i++) {
      c += s[Math.ceil(Math.random() * 15)]
    }
    return c
  }
}

Train of thought combing

  1. All lines have the same representation and can be abstracted into a Line class
    1. The length and color of each line are constant, and each line has a long tail effect during movement
  2. The movement of the line depends on the position of the mouse
    1. When the mouse moves, each line will follow

Main process

  1. The starting point of the initial line is in the middle of the canvas, and the length and color of each line are calculated randomly
  2. When preparing lines, draw lines from the center of the canvas outward according to the randomly calculated angle and line length (refer to step 4 for calculation method)
  3. After the animation starts, draw a new line starting from the end of the previous line
  4. Location of new line
    1. Mouse position dependent
    2. The length from the mouse position depends on the previously randomly calculated line length. It should be: mouse position + line length
    3. Because each line will have the effect of rotation, all the positions should have a certain angle change, otherwise the effect of line rotation cannot be realized
      1. Imagine a circle with radius r in a coordinate system
      2. Pull A line outward from the center of the circle and intersect the circle at point A (x, y) at an angle of θ
        1. From the trigonometric function, we can know that x = r * cos θ, y = r * sin θ
      3. angle θ When changes occur, the corresponding x and y will also change accordingly
    4. When the angle changes, the position of the new line should be
      1. X = mouse position x + line length * cos θ
      2. Y = mouse position Y + line length * sin θ
  5. Loop through step 3 and step 4 during animation

Long tail effect

The canvas code CTX. Is cleared clearRect(0, 0, canvas.width, canvas.heigth)

Change to the following code and let it repeatedly overwrite the previous style to achieve the long tail effect

ctx.fillStyle = 'rgba(0,0,0,0.05)'
ctx.fillRect(0, 0, canvas.width, canvas.heigth)

reference resources

https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Basic_animations

https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Advanced_animations

Topics: canvas