(四) JavaScript-Canvas-自由运动的球

JavaScript 是前端核心, 掌握这门语言是步入前端高手行列必经之路,噢,别忘了还有TypeScript, 学习它还需要OOP知识, 底层的浏览器原理、HTTP协议也必不可少, 此系列文章记录使用JavaScript和Canvas进行游戏开发, 有游戏才有趣!!!

效果图

(四) JavaScript-Canvas-自由运动的球

本节将使用window.requestAnimationFrame()函数制作动画,先从简单的开始, 理解以下内容需要对JavaScript 中的函数和class有基本的了解

  • 本节JavaScript 技术要点

    • 使用class定义类
    • 使用constructor定义构造函数
    • 使用Canvas Context的 arc函数绘制圆
    • 动画关键函数 window.requestAnimationFrame()

1. 准备HTML页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!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>Document</title>
<style>
*{padding: 0; margin: 0;}
#canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>

/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

</script>
</body>
</html>

2. 创建一个圆类

  • 使用ES6中的class关键字 定义一个圆(也可以直接使用function),一个圆需要中心坐标以及半径参数,在本例中还需要移动的距离

    • 使用class定义圆,x,y表示圆的中心坐标,dx,dy表示移动时的横坐标和纵坐标距离 , radius表示半径

      1
      2
      3
      4
      5
      6
      7
      8
      9
      class Circle {
      constructor(x, y, dx, dy, radius) {
      this.x = x
      this.y = y
      this.dx = dx
      this.dy = dy
      this.radius = radius
      }
      }
    • 使用function定义

      1
      2
      3
      4
      5
      6
      7
      function Circle(x, y, dx, dy, radius) {
      this.x = x
      this.y = y
      this.dx = dx
      this.dy = dy
      this.radius = radius
      }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!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>Document</title>
<style>
*{padding: 0; margin: 0;}
#canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

class Circle {
constructor(x, y, dx, dy, radius) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
}
}
</script>
</body>
</html>

3. 定义绘制圆的函数,在Circle类中定义

  • 绘制开始 beginPath()
  • 绘制圆的arc函数 ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI, false)
    • this.x,this.y是指当前对象的坐标
    • this.radius是指当前对象的半径
    • 0 表示开始角度
    • 2*Math.PI 表示360度
    • 最后个参数表示是否是逆时针方向绘制,设置为true或false均可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!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>Document</title>
<style>
*{padding: 0; margin: 0;}
#canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

class Circle {
constructor(x, y, dx, dy, radius) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
}
draw() {
ctx.beginPath()
ctx.arc(this.x,this.y,this.radius,0,2*Math.PI, false)
//线样式
ctx.strokeStyle = 'blue'
ctx.stroke()
//填充
ctx.fill()
}
}

</script>
</body>
</html>

4. 圆位置变化

  • 圆在Canvas中位置要不断的变化才能有动画效果,所以x和y坐标不是固定的,而是动态变化的,可以定义一个数不断的更新圆的x,y的值
  • 圆的位置不能超过Canvas的宽度和高度, 判断时需要将半径也算上
  • 增加的函数命名为update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!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>Document</title>
<style>
*{padding: 0; margin: 0;}
#canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

class Circle {
constructor(x, y, dx, dy, radius) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
}
//画一个圆
draw() {
ctx.beginPath()
ctx.arc(this.x,this.y,this.radius,0,2*Math.PI, false)
//线样式
ctx.strokeStyle = 'blue'
ctx.stroke()
//填充
ctx.fill()
}

//更新圆的位置
update() {
//判断圆是否超出canvas边界,判断时需要将半径也算上
if(this.x + this.radius > canvas.width || this.x-this.radius < 0){
//如果超出x轴左右两边的边界则重新设置 dx的值
this.dx = -this.dx
}
//高度边界判断
if(this.y+this.radius > canvas.height || this.y-this.radius < 0) {
this.dy = -this.dy
}
//更改x和y坐标
this.x += this.dx
this.y += this.dy

//调用draw()函数重新画圆
this.draw()
}
//创建一个圆
var circle = new Circle(200,200,3,3,30)
//调用update函数
circle.update()
}

</script>
</body>
</html>

效果如图,圆不会动,原因是update函数需要不断调用,此时只调用了一次

5. 动画

  • 使用动画函数不断自动调用update
  • 每次刷新update调用时都需要clear一下整个画布,试试不去调用clearRect的效果会怎样
  • 关于动画帧函数,参考 window.requestAnimationFrame()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!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>Document</title>
<style>
*{padding: 0; margin: 0;}
#canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

class Circle {
constructor(x, y, dx, dy, radius) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
}
//画一个圆
draw() {
ctx.beginPath()
ctx.arc(this.x,this.y,this.radius,0,2*Math.PI, false)
//线样式
ctx.strokeStyle = 'blue'
ctx.stroke()
//填充
ctx.fill()
}

//更新圆的位置
update() {
//判断圆是否超出canvas边界,判断时需要将半径也算上
if(this.x + this.radius > canvas.width || this.x-this.radius < 0){
//如果超出x轴左右两边的边界则重新设置 dx的值
this.dx = -this.dx
}
//高度边界判断
if(this.y+this.radius > canvas.height || this.y-this.radius < 0) {
this.dy = -this.dy
}
//更改x和y坐标
this.x += this.dx
this.y += this.dy

//调用draw()函数重新画圆
this.draw()
}
}


//创建一个圆
var circle = new Circle(200,200,3,3,30)
//调用update函数
circle.update()

//动画函数
function animate() {
//不断请求动画帧,每次请求都会重新调用一次animate,
requestAnimationFrame(animate)
ctx.clearRect(0,0,canvas.width,canvas.height)

circle.update()
}
animate()

</script>
</body>
</html>

效果如此文开头的动图,如果注释掉ctx.clearRect(0,0,canvas.width,canvas.height) 你将得到以下结果

加上数组,再加上随机颜色的变化,就可以产生下图的效果了

动画相对静态的图形稍麻烦一点,但若是掌握了requestAnimationFrame以及背后的原理,实现动画就简单多了,当然,也可以使用setTimeout或setInterval,大伙可以试试