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
<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