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.


s = v t 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


v t = v 0 + a t v_t = v_0 + at

Displacement formula of uniformly accelerated linear motion


x = v 0 + v t 2 t = v 0 t + 1 2 a t 2 x = \dfrac{v_0+v_t}{2}t = v_0t + \dfrac{1}{2}at^2

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


s = v t s = vt

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

x = v 0 + v t 2 t = v 0 t + 1 2 a t 2 x = \dfrac{v_0+v_t}{2}t = v_0t + \dfrac{1}{2}at^2

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

  1. 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
  2. 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!