This article has participated in the call for good writing activities, click to view: back end, big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!

This article will cover some applications of trigonometry in Canvas animation drawing, including:

  • Trigonometric functions
  • rotating
  • waveform
  • Circular and elliptical
  • The Pythagorean theorem
  • Distance between two points

Angle

Radians and angles

Before we begin, we need to familiarize ourselves with radians and angles, since trigonometric methods in JavaScript’s native Math object use radians.

I learned in high school that 360 degrees is equal to 2 PI radians, so the formula for converting angles to radians is:

Radians = Degrees * math.pi / 180 Degrees = Radians * 180 / math.piCopy the code

Canvas coordinates

Canvas The top left corner of the canvas is (0, 0). The diagram below:

In addition to the special coordinate system, its Angle measurement has its own characteristics, that is, clockwise is positive, counterclockwise is negative, as shown below:

Trigonometric functions

Trigonometric functions relate the interior angles of a right triangle to the ratios of its two sides, and can be defined equivalently in terms of the lengths of the various line segments associated with the unit circle.

Sine: Opposite over hypotenuse

For example, the sine of 30° is 0.5

console.log(Math.sin(30 * Math.PI / 180)) / / 0.49999999999999994
Copy the code

The reason it does not equal 0.5 is because JavaScript stores floating point numbers, which are not expanded here

Similarly, the sine of -30° is -0.5 as follows

console.log(Math.sin(-30 * Math.PI / 180)) // -0.49999999999999994
Copy the code

Cosine: adjacent over hypotenuse

console.log(Math.cos(30 * Math.PI / 180)) / / 0.8660254037844387
console.log(Math.cos(60 * Math.PI / 180)) / / 0.5000000000000001
Copy the code

Tangent: Opposite side over adjacent side

You have to pay attention to the positive and negative properties

console.log(Math.tan(45 * Math.PI / 180)) / / 0.9999999999999999
console.log(Math.tan(-45 * Math.PI / 180)) // -0.9999999999999999
Copy the code

Arcsine and arccosine

It’s simply the inverse of sine and cosine, you input a ratio, you get the radians of the corresponding Angle

Such as:

console.log(Math.asin(1/2) * 180 / Math.PI) / / 30.000000000000004
console.log(Math.acos(1/2) * 180 / Math.PI) / / 60.00000000000001
Copy the code

The arctangent

An arc tangent is the inverse of a tangent, given a ratio, that gives the radians of the corresponding angles

For example, the arctangent of triangles A and B is:

console.log(Math.atan(-1/-1) * 180 / Math.PI) / / 45
console.log(Math.atan(1/1) * 180 / Math.PI) / / 45
Copy the code

But now we can’t tell whether the arctangent corresponds to triangle A or triangle B. This requires another arctangent function, atan2, which can calculate the azimuth Angle. Atan2 takes two parameters, the length of the opposite side and the length of the adjacent side, for example:

console.log(Math.atan2(-1, -1) * 180 / Math.PI) / / - 135
console.log(Math.atan2(1.1) * 180 / Math.PI) / / 45
Copy the code

You can see that the first result is minus 135 degrees, which is exactly the azimuth of triangle A.

rotating

Having reviewed trigonometric functions, let’s do an exercise. Implement an arrow that always points to the mouse.

Always an arrow pointing to the mouse

First draw an Arrow and create an Arrow class

class Arrow {
  x: number

  y: number

  color: string

  rotation: number

  constructor() {
    this.x = 0
    this.y = 0
    this.color = '#42A5F5'
    this.rotation = 0
  }

  draw(context: CanvasRenderingContext2D) {
    context.save()
    context.translate(this.x, this.y)
    context.rotate(this.rotation)
    context.lineWidth = 2
    context.fillStyle = this.color
    context.beginPath()
    context.moveTo(-50, -25)
    context.lineTo(0, -25)
    context.lineTo(0, -50)
    context.lineTo(50.0)
    context.lineTo(0.50)
    context.lineTo(0.25)
    context.lineTo(-50.25)
    context.lineTo(-50, -25)
    context.closePath()
    context.fill()
    context.stroke()
    context.restore()
  }
}

export default Arrow
Copy the code

Create the

tag to display the arrow

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
Copy the code

Place the arrow in the center of the canvas

import Arrow from '.. /common/Arrow'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')
  const arrow = new Arrow()
  arrow.x = canvas.width / 2
  arrow.y = canvas.height / 2
  if (context) {
    arrow.draw(context)
  }
}
Copy the code

See the following effect:

To listen for mouse movements, create a util.ts file to add mouse position listening

const captureMouse = (element: HTMLElement) = > {
  const mouse: {
    x: number
    y: number
    event: MouseEvent | null
  } = {
    x: 0.y: 0.event: null,}const { offsetLeft, offsetTop } = element

  element.addEventListener('mousemove'.(e) = > {
    let x
    let y
    x = e.pageX
    y = e.pageY
    x -= offsetLeft
    y -= offsetTop
    mouse.x = x
    mouse.y = y
    mouse.event = e
  })
  return mouse
}

export { captureMouse }
Copy the code

Add mouse position listening to canvas main logic

import Arrow from '.. /common/Arrow'
import { captureMouse } from '.. /common/utils'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')
  const arrow = new Arrow()
  arrow.x = canvas.width / 2
  arrow.y = canvas.height / 2

  const pos = captureMouse(canvas) // Monitor mouse position

  if (context) {
    const drawFrame = () = > {
      window.requestAnimationFrame(drawFrame) // execute each frame
      context.clearRect(0.0, canvas.width, canvas.height) // Clear the canvas content
      if (pos.x && pos.y) {
        const dx = pos.x - arrow.x
        const dy = pos.y - arrow.y
        arrow.rotation = Math.atan2(dy, dx) // Here the azimuth is calculated
      }
      arrow.draw(context)
    }
    drawFrame()
  }
}
Copy the code

Ingenious use of azimuth arctangent function, the effect is as follows:

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

wave

A small ball in simple harmonic motion

We can use sine waves to create undamped harmonic vibrations, and we can use a circle that moves in horizontal harmonic motion. Start by declaring a Ball class with the following code

class Ball {
  radius: number

  color: string

  x: number

  y: number

  lineWidth: number

  constructor(radius: number = 40, color: string = '# 795548') {
    this.radius = radius
    this.color = color
    this.x = 0
    this.y = 0
    this.lineWidth = 1
  }

  /** * draw */
  public draw(context: CanvasRenderingContext2D) {
    context.save()
    context.translate(this.x, this.y)
    context.lineWidth = this.lineWidth
    context.fillStyle = this.color
    context.beginPath()
    context.arc(0.0.this.radius, 0.Math.PI * 2.true)
    context.closePath()
    context.fill()
    if (this.lineWidth > 0) {
      context.stroke()
    }
    context.restore()
  }
}

export default Ball
Copy the code

Draw it onto the canvas and see what it looks like

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
Copy the code
import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball()
    ball.x = canvas.width / 2
    ball.y = canvas.height / 2
    ball.draw(context)
  }
}
Copy the code

The effect is as follows:

Now let’s make it move harmonically in the horizontal direction

import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball()
    ball.x = canvas.width / 2
    ball.y = canvas.height / 2
    let angle = 0

    const drawFrame = () = > {
      window.requestAnimationFrame(drawFrame)
      context.clearRect(0.0, canvas.width, canvas.height)
      // Sine motion
      ball.x = canvas.width / 2 + Math.sin(angle) * 50
      angle += 0.1 // The Angle is increasing
      ball.draw(context)
    }
    drawFrame()
  }
}
Copy the code

The effect is as follows:

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

Maintain the same speed under high brush

Most monitors have a refresh rate of 60Hz, but as technology advances and more and more 120Hz and 144Hz display devices are available, we need to keep the same speed and keep the smoothness of the high brush screen. Therefore, if the above code is not changed, it will move twice as fast on a 120Hz screen as on a 60Hz screen. We can solve this problem by multiplying the time per frame by the speed.

import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball()
    ball.x = canvas.width / 2
    ball.y = canvas.height / 2
    const speed = 4 / / speed
    let angle = 0

    let then = 0
    // The input of raf cb is the time of the current frame
    const drawFrame = (time: number) = > {
      const timeInSeconds = time * 0.001
      const deltaTimeInSeconds = timeInSeconds - then // Each frame takes time, in seconds
      then = timeInSeconds

      window.requestAnimationFrame(drawFrame)
      context.clearRect(0.0, canvas.width, canvas.height)
      ball.x = canvas.width / 2 + Math.sin(angle) * 50
      angle += speed * deltaTimeInSeconds // Displacement = speed * time per frame
      ball.draw(context)
    }
    drawFrame(0)}}Copy the code

The effect is as follows:

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

Sinusoidal trajectory

We still use the Ball class and set its radius to be very small, keeping its trajectory

import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball(1)
    ball.y = canvas.height
    ball.x = 0
    const speedY = 2
    const speedX = 50

    let angle = 0

    let then = 0
    const drawFrame = (time: number) = > {
      const timeInSeconds = time * 0.001
      const deltaTimeInSeconds = timeInSeconds - then
      then = timeInSeconds
      ball.x += speedX * deltaTimeInSeconds // Horizontal displacement
      ball.y = canvas.height / 2 + Math.sin(angle) * 50 // Vertical displacement
      angle += speedY * deltaTimeInSeconds
      ball.draw(context)
      window.requestAnimationFrame(drawFrame)
    }
    drawFrame(0)}}Copy the code

The effect is as follows:

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

Circular and elliptical

If we know the radius of the circle, then the coordinates of a point on the circle are zero

X is equal to r cosine theta, y is equal to r sine thetaCopy the code

And from that we can do circular motion

Circular motion

According to the above formula, set the radius to 100 as follows

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
Copy the code
import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball(5)
    const ball2 = new Ball(50.'#3949AB')
    ball.x = canvas.width / 2
    ball.y = canvas.height / 2
    ball2.x = canvas.width / 2
    ball2.y = canvas.height / 2
    const speed = 2
    const r = 100

    let angle = 0

    let then = 0
    const drawFrame = (time: number) = > {
      context.clearRect(0.0, canvas.width, canvas.height)
      const timeInSeconds = time * 0.001
      const deltaTimeInSeconds = timeInSeconds - then
      then = timeInSeconds
      ball.x = canvas.width / 2 + r * Math.cos(angle)
      ball.y = canvas.height / 2 + r * Math.sin(angle)
      angle += speed * deltaTimeInSeconds
      ball.draw(context)
      ball2.draw(context)
      window.requestAnimationFrame(drawFrame)
    }
    drawFrame(0)}}Copy the code

Results the following

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

Perielliptic motion

The radius in the x direction and the radius in the y direction should be set respectively.

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
Copy the code
import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball(5)
    const ball2 = new Ball(50.'#3949AB')
    ball.x = canvas.width / 2
    ball.y = canvas.height / 2
    ball2.x = canvas.width / 2
    ball2.y = canvas.height / 2
    const speed = 2
    const rx = 100
    const ry = 60

    let angle = 0

    let then = 0
    const drawFrame = (time: number) = > {
      context.clearRect(0.0, canvas.width, canvas.height)
      const timeInSeconds = time * 0.001
      const deltaTimeInSeconds = timeInSeconds - then
      then = timeInSeconds
      ball.x = canvas.width / 2 + rx * Math.cos(angle)
      ball.y = canvas.height / 2 + ry * Math.sin(angle)
      angle += speed * deltaTimeInSeconds
      ball.draw(context)
      ball2.draw(context)
      window.requestAnimationFrame(drawFrame)
    }
    drawFrame(0)}}Copy the code

Results the following

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

The Pythagorean theorem

Pythagorean theorem: The sum of squares of the lengths of two sides of a right triangle on a plane is equal to the square of the length of the hypotenuse.

c ^ 2 = a ^ 2 + b ^ 2
Copy the code

We mainly use the Pythagorean theorem to calculate the distance between the bright spots.

Distance between two points

Declare 2 small circles, place them randomly on the canvas, and calculate the distance between the two circles’ centers

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
<div id="distance">distance is: </div>
Copy the code
import Ball from '.. /common/Ball'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const distanceDom: HTMLDivElement | null = document.querySelector('#distance')

if (canvas) {
  const context = canvas.getContext('2d')

  if (context) {
    const ball = new Ball(5.'#AED581')
    const ball2 = new Ball(5.'#3949AB')
    const pos1 = {
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
    }
    const pos2 = {
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
    }
    ball.x = pos1.x
    ball.y = pos1.y
    ball2.x = pos2.x
    ball2.y = pos2.y

    const drawFrame = () = > {
      ball.draw(context)
      ball2.draw(context)
      const dx = ball.x - ball2.x
      const dy = ball.y - ball2.y
      const distance = Math.sqrt(dx ** 2 + dy ** 2)
      if (distanceDom) {
        distanceDom.insertAdjacentHTML('beforeend'.String(distance))
      }
    }
    drawFrame()
  }
}
Copy the code

Results the following

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

The distance between a mouse pointer and a point

To make this more intuitive, we can look at the distance between the mouse pointer and the point and draw a line

<canvas id="mainCanvas" style="background-color: #fff;" width="800" height="400"></canvas>
<div id="distance"></div>
Copy the code
import Ball from '.. /common/Ball'
import { captureMouse } from '.. /common/utils'

const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const distanceDom: HTMLDivElement | null = document.querySelector('#distance')

if (canvas) {
  const context = canvas.getContext('2d')
  const mousePos = captureMouse(canvas)

  if (context) {
    const ball = new Ball(5.'#AED581')
    const pos1 = {
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
    }
    ball.x = pos1.x
    ball.y = pos1.y

    const drawFrame = () = > {
      context.clearRect(0.0, canvas.width, canvas.height)

      ball.draw(context)
      const dx = ball.x - mousePos.x
      const dy = ball.y - mousePos.y
      const distance = Math.sqrt(dx ** 2 + dy ** 2)
      if (distanceDom) {
        distanceDom.innerHTML = String(distance)
      }

      // Draw a line from the mouse pointer to the specified point
      context.save()
      context.moveTo(ball.x, ball.y)
      context.lineTo(mousePos.x, mousePos.y)
      context.closePath()
      context.stroke()

      window.requestAnimationFrame(drawFrame)
    }
    drawFrame()
  }
}
Copy the code

Results the following

The demo link gaohaoyang. Making. IO/canvas – prac…

Source link github.com/Gaohaoyang/…

conclusion

In this paper, we mainly studied some applications of trigonometry in Canvas drawing, including the conversion of Angle and radian, features of Canvas 2D coordinate system, trigonometric function, rotation, wave, circular and elliptical motion, calculation of distance between two points, etc. This provides the foundation and aid for the development of more complex interactive scenarios.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  arrow.rotation = Math.atan2(dy, X = canvas. Width / 2 + math.sin (Angle) * 50 Angle += speed * deltaTimeInSeconds // displacement = speed * X = canvas. Width / 2 + r * math. cos(Angle) ball. Y = canvas. Height / 2 + r * math. sin(Angle) Angle += Speed * deltaTimeInSeconds Ball. X = Canvas. Width / 2 + rx * math. cos(Angle) ball Math.sin(Angle) Angle += speed * deltaTimeInSeconds Distance between two points const dx = ball.x-ball2.x const dy = ball.y-ball2.y const distance = Math.sqrt(dx ** 2 + dy ** 2)Copy the code