前言
在屏幕可视区域内创建多个小球,每个小球的颜色、大小和初始速度都是随机的。
初始化的时候,小球会随机出现在屏幕中的任意位置,碰撞到水平线或者垂直线上(屏幕边缘),会对速度产生一定的影响,因为重力效果,小球最终会停留在水平线上,或者以一定的速度在水平线上左右运动。
效果预览
实现步骤
画一个小球
1
2
3
4
5
6
7
8
|
// 获取元素
const canvas = document.querySelector('canvas');
// 设置画布的宽高 不能通过CSS设置
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// 获取上下文 Canvas所有API(绘制图形)都是基于上下文
const ctx = canvas.getContext("2d");
|
定义创建球的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function Circle(x, y, radius, color) {
// 坐标点x,y 半径 颜色
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.draw = function () {
ctx.beginPath(); // 绘制路径 开始
ctx.arc(this.x, this.y, this.radius, Math.PI * 2, false); // 画一个整圆,半圆是Math.PI
ctx.fillStyle = this.color; // 设置要填充的颜色
ctx.fill(); // 给图形填充颜色
ctx.closePath(); //绘制路径结束
};
}
|
调用方法实例化一个球
1
2
|
var ball = new Circle(100, 100, 30, "blue");
ball.draw();
|
这样,就完成了一个小球的绘制。
如果Canvas画布并不等于屏幕可视区域,屏幕边缘有滚动条,怎么解决?
1
2
3
4
5
|
/* 清除默认样式 */
body {
margin: 0;
overflow: hidden;
}
|
让球动起来
更新球的位置,限制球在屏幕内跳动
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
|
function Circle(x, y, dx, dy, radius, color) {
// 坐标点x,y 半径 颜色
// dx x轴方向的速度
// dy y轴方向的速度
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = color;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
};
this.update = function () {
// 控制球在可视范围内/Canvas画布内
if (this.y + this.radius + this.dy > canvas.height || this.y - this.radius <= 0) {
this.dy = -this.dy;
}
// x方向 撞击到屏幕墙壁 就反向
if (
this.x + this.radius + this.dx > canvas.width ||
this.x - this.radius <= 0
) {
// this.x + this.radius + this.dx 是判断下一次move 是否会超过边界
this.dx = -this.dx;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
|
使用requestAnimationFrame,让动画按照屏幕帧率执行
1
2
3
4
5
6
7
8
|
var ball = new Circle(100, 100, 3, 1, 30, "blue");
function animate() {
requestAnimationFrame(animate);
// 每屏绘制都要清除上一次的绘制 不然就会一直叠加 会把行动轨迹都绘制出来
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
ball.update();
}
animate();
|
增加重力效果和摩擦系数
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
|
var friction = 0.9; // 摩擦系数 系数越大 这些球恢复平静的时间就越久
var gravity = 1; // 重力
function Circle(x, y, dx, dy, radius, color) {
// 坐标点x,y 半径 颜色
// dx x轴方向的速度
// dy y轴方向的速度
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = color;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.stroke(); // 加上边框
ctx.closePath();
};
this.update = function () {
if (this.y - this.radius <= 0) {
this.dy = -this.dy;
}
if (this.y + this.radius + this.dy > canvas.height) {
// 每次撞击到地面,速度会降低(摩擦力)
this.dy = -this.dy * friction;
} else {
// 在空中的时候 都有一个垂直方向的重力 对y轴速度产生影响
this.dy += gravity;
}
// x方向 撞击到屏幕墙壁 就反向
if (
this.x + this.radius + this.dx > canvas.width ||
this.x - this.radius <= 0
) {
// this.x + this.radius + this.dx 是判断下一次move 是否会超过边界
this.dx = -this.dx;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
|
创建多个球 分散在屏幕任意位置
获取任意范围内的整数
1
2
3
|
function randomIntFromRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
|
获取任意颜色
1
2
3
|
function getRandomColor(colors) {
return colors[Math.floor(Math.random() * colors.length)];
}
|
给多个球分配任意大小、速度和屏幕内任意位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
let ballArray = [];
const colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2"]; // 一些好看的颜色合集
function init() {
for (let i = 0; i < 100; i++) {
let radius = randomIntFromRange(2, 20);
let x = randomIntFromRange(radius, window.innerWidth - radius); // 不能溢出屏幕
let y = randomIntFromRange(radius, window.innerHeight - radius);
let dx = randomIntFromRange(-2, 2);
let dy = randomIntFromRange(-2, 2);
let color = getRandomColor(colors);
ballArray.push(new Circle(x, y, dx, dy, radius, color));
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
function animate() {
requestAnimationFrame(animate);
// 每屏绘制都要清除上一次的绘制 不然就会一直叠加 会把行动轨迹都绘制出来
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
// ball.update();
for (let i = 0; i < ballArray.length; i++) {
ballArray[i].update();
}
}
init();
animate();
|
点击屏幕任意位置,再次实现动效
1
2
3
|
window.addEventListener("click", function () {
init();
});
|
屏幕自适应
1
2
3
4
5
|
window.addEventListener("resize", function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
init();
});
|
源码链接
最后附上Codepen 在线地址
,喜欢的可以点个💛
Job searching can be exhausting, and I’m not sure if I’ll find my dream job. So, for now, let’s take a break and enjoy some fun coding