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 begin with the animation programming section, starting with the basic motion properties: velocity, vectors, and acceleration
- speed
- The acceleration
The velocity vector
The velocity vector is the velocity in some direction. This contains the value and direction of the velocity (both magnitude and direction).
Any velocity vector can again be decomposed into x and y directions.
In the following examples, we will use vx to represent the velocity vector along the x axis and vy to represent the velocity vector along the y axis.
Uniform motion in a straight line
Uniform linear motion refers to the motion along a straight line with the same speed (speed). In uniform linear motion, distance is proportional to time, calculated by the formula S =vt.
Here we reuse the Ball class created in the article trigonometry in Animation for the example of uniform linear motion of a Ball
We know the motion at x and y minutes
The following code
import stats from '.. /.. /common/stats'
import Ball from '.. /.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const vx = 10 // X speed, 10 pixels /s
const vy = 20 // Y speed, 20 pixels /s
const x0 = 20 // The initial position
const y0 = 20
if (canvas) {
canvas.width = window.screen.width
canvas.height = window.screen.height
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
if (context) {
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
context.clearRect(0.0, canvas.width, canvas.height)
ball.x = vx * timeInSeconds + x0
ball.y = vy * timeInSeconds + y0
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The core code is
ball.x = vx * timeInSeconds + x0
ball.y = vy * timeInSeconds + y0
Copy the code
Results the following
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Based on the velocity vector motion
The above is calculated by the components of velocity Vx and vy. If the components of velocity are unknown and only the ball moves at 10 pixels per second at 45 degrees, how can this be achieved? This is where we use trigonometry, and if you don’t know trigonometry, look back at the previous article on Trigonometry in Animation.
The component is
vx = 10 * cos(45)
vy = 10 * sin(45)
Copy the code
So the complete code is as follows
import stats from '.. /common/stats'
import Ball from '.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const alpha = 45 // The Angle is 45 degrees
const v = 10 // Speed, 10 pixels /s
const x0 = 20 // The initial position
const y0 = 20
if (canvas) {
canvas.width = window.screen.width
canvas.height = window.screen.height
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
if (context) {
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
context.clearRect(0.0, canvas.width, canvas.height)
ball.x = v * Math.cos((alpha * Math.PI) / 180) * timeInSeconds + x0
ball.y = v * Math.sin((alpha * Math.PI) / 180) * timeInSeconds + y0
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Note that timeInSeconds is a continuous amount of time, and it is possible to use time fragments of each frame frequently in a Canvas drawing for calculation, as discussed below.
Implementation based on each frame interval
import stats from '.. /common/stats'
import Ball from '.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const alpha = 45 // The Angle is 45 degrees
const v = 10 // Speed, 10 pixels /s
const x0 = 20 // The initial position
const y0 = 20
if (canvas) {
canvas.width = window.screen.width
canvas.height = window.screen.height
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
ball.x = x0
ball.y = y0
if (context) {
let then = 0
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTime = timeInSeconds - then
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
ball.x += v * Math.cos((alpha * Math.PI) / 180) * deltaTime
ball.y += v * Math.sin((alpha * Math.PI) / 180) * deltaTime
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The effect is the same as in the previous demo, where the core logic is
Each time the displacement within each frame is calculated and then summed up
ball.x += v * Math.cos((alpha * Math.PI) / 180) * deltaTime
ball.y += v * Math.sin((alpha * Math.PI) / 180) * deltaTime
Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Move to the click position
In trigonometry in Animation, we implemented an arrow that always points to the mouse, and now we modify it slightly to always move to the click position.
import stats from '.. /common/stats'
import Arrow from '.. /common/Arrow'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const v = 100 // The speed is 10 pixels /s
/** * get the mouse click position */
const getClickPos = (element: HTMLElement) = > {
const pos = {
x: 0.y: 0,
}
element.addEventListener('click'.(e: MouseEvent) = > {
pos.x = e.pageX
pos.y = e.pageY
})
return pos
}
if (canvas) {
canvas.width = window.screen.width
canvas.height = window.screen.height
const context = canvas.getContext('2d')
const arrow = new Arrow()
arrow.x = canvas.width / 2
arrow.y = canvas.height / 2
const mousePos = getClickPos(canvas)
let then = 0
if (context) {
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTimeInSeconds = timeInSeconds - then // The interval of each frame, in seconds
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
const dx = mousePos.x - arrow.x
const dy = mousePos.y - arrow.y
const angle = Math.atan2(dy, dx)
arrow.x += v * Math.cos(angle) * deltaTimeInSeconds
arrow.y += v * Math.sin(angle) * deltaTimeInSeconds
arrow.rotation = angle
arrow.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Always follow the mouse arrow
To implement the demo, simply replace the click event above with the Mousemove event
import stats from '.. /common/stats'
import Arrow from '.. /common/Arrow'
import { captureMouse } from '.. /common/utils'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const v = 100 // The speed is 10 pixels /s
if (canvas) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const context = canvas.getContext('2d')
const arrow = new Arrow()
arrow.x = canvas.width / 2
arrow.y = canvas.height / 2
const mousePos = captureMouse(canvas)
let then = 0
if (context) {
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTimeInSeconds = timeInSeconds - then // The interval of each frame, in seconds
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
const dx = mousePos.x - arrow.x
const dy = mousePos.y - arrow.y
const angle = Math.atan2(dy, dx)
arrow.x += v * Math.cos(angle) * deltaTimeInSeconds
arrow.y += v * Math.sin(angle) * deltaTimeInSeconds
arrow.rotation = angle
arrow.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
The acceleration
In middle school physics, the displacement of accelerated motion is generally calculated by continuous time variable, but in canvas drawing, we mostly use the time segment between each frame. Let’s first review acceleration based on continuous time.
Acceleration based on continuous time
Velocity formula for uniformly accelerated linear motion
Displacement formula of uniformly accelerated linear motion
The sample
import stats from '.. /common/stats'
import Ball from '.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const v0x = 60 // Initial speed in x direction, pixel /s
const v0y = 0 // Initial speed in x direction, pixel /s
const ax = 0 // Acceleration in x direction, pixel /s^2
const ay = 600 // Acceleration in y direction, pixel /s^2
const x0 = 60 // The initial position
const y0 = 20
if (canvas) {
canvas.width = window.screen.width
canvas.height = window.screen.height
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
if (context) {
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
context.clearRect(0.0, canvas.width, canvas.height)
ball.x = v0x * timeInSeconds + (1 / 2) * ax * timeInSeconds ** 2 + x0
ball.y = v0y * timeInSeconds + (1 / 2) * ay * timeInSeconds ** 2 + y0
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
Results the following
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Accelerated motion based on time per frame interval
import stats from '.. /common/stats'
import Ball from '.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const v0x = 60 // Initial speed in x direction, pixel /s
const v0y = 0 // Initial speed in x direction, pixel /s
const ax = 0 // Acceleration in x direction, pixel /s^2
const ay = 600 // Acceleration in y direction, pixel /s^2
const x0 = 60 // The initial position
const y0 = 20
if (canvas) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
ball.x = x0
ball.y = y0
let vx = v0x
let vy = v0y
if (context) {
let then = 0
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTime = timeInSeconds - then
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
vx += ax * deltaTime
vy += ay * deltaTime
ball.x += vx * deltaTime
ball.y += vy * deltaTime
// ball.x = v0x * timeInSeconds + (1 / 2) * ax * timeInSeconds ** 2 + x0
// ball.y = v0y * timeInSeconds + (1 / 2) * ay * timeInSeconds ** 2 + y0
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Use directional keys to control ball acceleration
import stats from '.. /common/stats'
import Ball from '.. /common/Ball'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const v0x = 0 // Initial speed in x direction, pixel /s
const v0y = 0 // Initial speed in x direction, pixel /s
let ax = 0 // Acceleration in x direction, pixel /s^2
let ay = 0 // Acceleration in y direction, pixel /s^2
const x0 = window.innerWidth / 2 // The initial position
const y0 = window.innerHeight / 2
if (canvas) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
document.addEventListener('keydown'.(e: KeyboardEvent) = > {
console.log(e.key)
switch (e.key) {
case 'ArrowLeft':
ax = -100
break
case 'ArrowRight':
ax = 100
break
case 'ArrowUp':
ay = -100
break
case 'ArrowDown':
ay = 100
break
default:
break}})document.addEventListener('keyup'.() = > {
ax = 0
ay = 0
})
const context = canvas.getContext('2d')
const ball = new Ball(10.'#1E88E5')
ball.x = x0
ball.y = y0
let vx = v0x
let vy = v0y
if (context) {
let then = 0
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTime = timeInSeconds - then
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
vx += ax * deltaTime
vy += ay * deltaTime
ball.x += vx * deltaTime
ball.y += vy * deltaTime
ball.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
Add gravity acceleration
In the real world, objects always experience a downward force of gravity. If we wanted to simulate this scenario, we would simply add two lines of code from the previous example so that the ball always has a downward acceleration, as follows
const gravity = 50. vy += gravity * deltaTimeCopy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
The spaceship
Use what you learned earlier in this chapter to create an ability to simulate the normal flight of a spacecraft
First draw the spaceship and create a new Ship class with the following code
/* eslint-disable no-param-reassign */
class Ship {
x = 0
y = 0
width = 25
height = 20
rotation = 0
showFlame = true
/** * draw */
public draw(c: CanvasRenderingContext2D) {
c.save()
c.translate(this.x, this.y)
c.rotate(this.rotation)
c.lineWidth = 1
c.strokeStyle = '#ffffff'
c.beginPath()
c.moveTo(10.0)
c.lineTo(-10.10)
c.lineTo(-5.0)
c.lineTo(-10, -10)
c.lineTo(10.0)
c.stroke()
if (this.showFlame) {
c.beginPath()
c.moveTo(-7.5, -5)
c.lineTo(-15.0)
c.lineTo(-7.5.5)
c.stroke()
}
c.restore()
}
}
export default Ship
Copy the code
Let’s draw it on the canvas
import Ship from '.. /common/Ship'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const x0 = window.innerWidth / 2 // The initial position
const y0 = window.innerHeight / 2
if (canvas) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const context = canvas.getContext('2d')
const ship = new Ship()
ship.x = x0
ship.y = y0
if (context) {
ship.draw(context)
}
}
Copy the code
Results the following
When there is a jet of flame:
Next to write the control logic, the spacecraft has 3 control methods left turn, right turn, ignition, the code is as follows:
import stats from '.. /common/stats'
import Ship from '.. /common/Ship'
const canvas: HTMLCanvasElement | null = document.querySelector('#mainCanvas')
const aRotation = 80 // Angular acceleration of spacecraft rotation
const aThrust = 50 // Push acceleration
const x0 = window.innerWidth / 2 // The initial position
const y0 = window.innerHeight / 2
let aRotationShip = 0 // Angular acceleration of rotation
let vRotationShip = 0 // Rotate the angular velocity
let aThrustShip = 0 // Push acceleration
let vThrustShip = 0 // Push speed
if (canvas) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const ship = new Ship()
ship.x = x0
ship.y = y0
document.addEventListener('keydown'.(e: KeyboardEvent) = > {
console.log(e.key)
switch (e.key) {
case 'ArrowLeft':
aRotationShip = -aRotation
break
case 'ArrowRight':
aRotationShip = aRotation
break
case 'ArrowUp':
aThrustShip = aThrust
ship.showFlame = true
break
case 'ArrowDown':
aThrustShip = -aThrust
break
default:
break}})document.addEventListener('keyup'.() = > {
aRotationShip = 0
aThrustShip = 0
ship.showFlame = false
})
const context = canvas.getContext('2d')
if (context) {
let then = 0
const drawFrame = (time: number) = > {
stats.begin()
const timeInSeconds = time / 1000 // Convert milliseconds to seconds
const deltaTime = timeInSeconds - then
then = timeInSeconds
context.clearRect(0.0, canvas.width, canvas.height)
vRotationShip += aRotationShip * deltaTime
ship.rotation += (vRotationShip * deltaTime * Math.PI) / 180
vThrustShip += aThrustShip * deltaTime
if (vThrustShip <= 0) {
vThrustShip = 0
}
const angle = ship.rotation
ship.x += vThrustShip * deltaTime * Math.cos(angle)
ship.y += vThrustShip * deltaTime * Math.sin(angle)
ship.draw(context)
stats.end()
window.requestAnimationFrame(drawFrame)
}
drawFrame(0)}}Copy the code
The demo link gaohaoyang. Making. IO/canvas – prac…
Source link github.com/Gaohaoyang/…
As you can see, the code listens for keyboard events and assigns the constant value of the angular acceleration of rotation to the real ship. Then calculate the rotation Angle of each frame based on angular acceleration
vRotationShip += aRotationShip * deltaTime // Calculate the angular velocity
ship.rotation += (vRotationShip * deltaTime * Math.PI) / 180 // Calculate the Angle
Copy the code
For the spacecraft ejection logic, we also listen for keyboard events and assign constant propulsion acceleration to the spacecraft’s propulsion acceleration. Then the velocity of the spacecraft is calculated by the acceleration of the spacecraft.
vThrustShip += aThrustShip * deltaTime // Calculate the speed of the spacecraft
if (vThrustShip <= 0) {
vThrustShip = 0
}
Copy the code
Notice here we’ve added the judgment that the shuttle can keep slowing down, but it can’t go backwards. Then according to the speed and Angle of the spacecraft, calculate the displacement of the spacecraft in the x and y directions
const angle = ship.rotation
ship.x += vThrustShip * deltaTime * Math.cos(angle)
ship.y += vThrustShip * deltaTime * Math.sin(angle)
Copy the code
conclusion
Uniform motion in a straight line
S = v * t + s0 // Velocity times time plus initial positionCopy the code
namely
The method of obtaining the velocity component
Vx is equal to the velocity in some direction times cosine of the Angle in that direction vy is equal to the velocity in some direction times sine of the Angle in that direction ball. X is equal to v starMath.cos((alpha * Math.PI) / 180) * timeInSeconds + x0
ball.y = v * Math.sin((alpha * Math.PI) / 180) * timeInSeconds + y0
Copy the code
The acceleration
s = v0 * t + (1 / 2) * a * t ^ 2
Copy the code
But in real animation development, the time interval of each frame is often used to calculate the displacement of objects in the frame. This has two advantages
- It is convenient to process real-time changes within each frame, such as acceleration changes and velocity changes, and directly obtain the displacement situation within the current frame for accumulation
- Keep things moving at the same speed on phones with different refresh rates
So the velocity of the body can generally be expressed as follows
x += v * deltaTime
Copy the code
For velocity vectors, trigonometric functions can be used to calculate the displacement of the object
x += v * deltaTime * Math.cos(angle)
y += v * deltaTime * Math.sin(angle)
Copy the code
If you’re accelerating, you can figure out the acceleration and figure out the velocity, and then you can use the velocity to figure out the displacement
v += a * deltaTime
x += v * deltaTime
Copy the code
In the final spacecraft example, we also introduced angular acceleration, which, like normal acceleration, is converted to angular velocity and then to Angle.
vRotation += aRotation * deltaTime // Calculate the angular velocity
ship.rotation += (vRotation * deltaTime * Math.PI) / 180 // Calculate the Angle
Copy the code
In addition, in the example we introduced the concept of gravitational acceleration, which is nothing more than adding an acceleration variable to the calculation of velocity in the y direction
const gravity = 50. vy += gravity * deltaTimeCopy the code
After learning the concepts of speed and acceleration and how to calculate them, you can develop more animations later!