【Canvas入门】从零开始在Canvas上绘制简单的动画
这篇文章是观看HTML5 Canvas Tutorials for Beginners教程做的记录,所以代码和最后的效果比较相似,教程的内容主要关于这四个部分:
- 创建并设置尺寸
- 添加元素
- 让元素动起来
- 与元素交互
设置Canvas的大小
获取到canvas并设置尺寸为当前窗口的大小:
var canvas = document.getElementById(canvas_id);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
获取到二维的Context并赋值给变量c,后面对canvas的操作主要都是通过c进行的:
var c = canvas.getContext("2d")
填充矩形
在(x,y)坐标处,填充一个宽为width,高为height的矩形:
c.fillRect(x, y, width, height)
设定填充矩形的颜色:
c.fillStyle = "green"; // 填充的颜色为绿色
c.fillRect(100, 100, 100, 100) // 在(100,100)坐标处填充一个边长为100的正方形
绘制线段
c.beginPath(); // 开始绘制路径
c.moveTo(100, 200); // 相当于笔尖在空中移动到(x,y)坐标的位置,也就是
c.lineTo(200, 300); // 让笔尖从之前的坐标到现在的坐标(200,300)画一条线段
c.strokeStyle = "#4455ff"; // 设置线条的颜色
c.stroke(); // 渲染到Canvas上
绘制圆形
,以(x,y)为圆心,radius为半径画圆,startAngle, endAngle分别为起点角度和终点角度(弧度制),最后一个参数是可选的counterclockwise,true为逆时针,false顺时针,默认为false:
arc(x, y, radius, startAngle, endAngle)
arc(x, y, radius, startAngle, endAngle, counterclockwise)
在 (100, 100) 处画一个半径为30,顺时针的圆:
c.beginPath();
c.arc(100, 100, 30, 0, 2 * Math.PI, false);
c.strokeStyle = "#00ff00";
c.stroke();
使用for循环生成随机位置的圆形
Math.random()
生成0-1之间的随机数,所以要乘上宽度和高度来让它的随机范围扩大到整个屏幕,也就是得到了很多个随机的坐标。
for (var i = 0; i < 100; i++) {
var x = Math.random() * window.innerWidth;
var y = Math.random() * window.innerHeight;
var r = Math.random() * 100; // 半径也随机生成,范围在0-100
c.beginPath();
c.arc(x, y, r, 0, 2 * Math.PI);
c.strokeStyle = "#0fff00";
c.stroke();
}
这样就生成了100个位置随机,大小随机的圆形。
加入动画
定义一个函数 animate()
并在内部调用,这样就得到了一个一直重复运行的函数,可以在这个函数里面更新元素的状态。
设置初始坐标为 (200, 200),x方向的速度
v
x
v_x
vx为2,y方向的速度
v
y
v_y
vy为0,在每一次刷新的时候使用速度来更新位置。
我们知道匀速运动时
x
=
v
t
x=vt
x=vt,所以可以通过速度改变位移。还可以通过加速度
a
a
a来改变速度
v
v
v,在Canvas中实现变速运动。
var x = 200, y = 200;
var vx = 2, vy = 0;
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, window.innerWidth, window.innerHeight); // 清除掉前一帧绘制的内容,否则会不断的重叠
c.beginPath();
c.arc(x, y, 30, 0, 2 * Math.PI);
c.stroke();
x += vx;
y += vy;
}
animate();
更多会动的圆
定义一个类,用来画一个圆心在 (200, 200) 处,半径为30的圆:
class Circle{
constructor(x, y) { // 在 constructor 中完成一些值的初始化
this.x = x;
this.y = y;
}
draw() {
c.beginPath();
c.arc(this.x, this.y, 30, 0, 2 * Math.PI);
c.stroke();
}
}
var circle = new Circle(200, 200);
// 当调用 new Circle(200, 200); 时,会将类的参数列表传入constructor()的参数列表,并执行constructor()
circle.draw()
接下来把更新 (x, y) 坐标的部分写成Circle类的update()
函数,并把 draw()
方法也包含进来:
class Circle{
constructor(x, y, vx, vy, radius) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = radius;
}
draw() { // 根据 x,y 坐标,半径 radius 画一个圆
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
c.stroke();
}
update() { // 更新 x,y 坐标, 将越界时反弹, 更新后绘制圆形
if (this.x - this.radius < 0 || this.x + this.radius > window.innerWidth) this.vx = -this.vx;
if (this.y - this.radius < 0 || this.y + this.radius > window.innerHeight) this.vy = -this.vy;
this.x += this.vx;
this.y += this.vy;
this.draw();
}
}
var circle = new Circle(200, 200, 2, 2, 30);
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
circle.update(); // 这边只需要一次次的调用 update() 即可
}
有了Circle类,就可以方便的创建很多个Circle对象,并更新它们的位置。
var circleArray = [];
for (var i = 0; i < 50; i++) {
var x = Math.random() * window.innerWidth;
var y = Math.random() * window.innerHeight;
var vx = (Math.random() - 0.5) * 3;
var vy = (Math.random() - 0.5) * 3;
var radius = Math.random() * 100;
circleArray.push(new Circle(x, y, vx, vy, radius))
}
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].update();
}
}
animate();
与鼠标交互
给移动的小球增加与鼠标交互的动画,当鼠标靠近时增大小球的半径,当鼠标远离时,再让它缩小,需要监听mousemove事件,并更新mouse.x和mouse.y的值:
window.addEventListener('mousemove', function (event) {
mouse.x = event.clientX;
mouse.y = event.clientY;
})
然后在update()
里面:
if (Math.sqrt((this.x - mouse.x) ** 2 + (this.y - mouse.y) ** 2) < 100) {
if (this.radius < maxRadius)
this.radius += 1;
} else if (this.radius > minRadius) {
this.radius -= 1;
}
为了方便使用,可以提前设定好最小半径和最大半径:
const maxRadius = 40, minRadius = 2;
适配不同尺寸的屏幕
当窗口大小发生变化时,会触发resize事件,可以通过监听这个事件,来重新初始化Canvas中的小球。
window.addEventListener('resize', function (event) {
init();
})
如果小球的数量是固定的,那么较小的屏幕上会太密集,而较大的屏幕上看起来又会太稀疏,所以可以根据屏幕的大小来设置小球的个数,我这里的做法是将窗口的面积除以5000并向下取整来计算出小球的个数:
var circleArray = [];
function init() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ballNumber = Math.floor((window.innerWidth * window.innerHeight) / 5000);
circleArray = [];
for (var i = 0; i < ballNumber; i++) {
var x = Math.random() * window.innerWidth;
var y = Math.random() * window.innerHeight;
var vx = (Math.random() - 0.5) * 3;
var vy = (Math.random() - 0.5) * 3;
var radius = Math.random() * minRadius + 1;//Math.random() * 20;
circleArray.push(new Circle(x, y, vx, vy, radius))
}
}
为了让小球更好看一些,可以为它们填充不同的颜色:
const colorArray = ["#AFD3E2", "#F2B6A0", "#FC4F00", "#FFEEB3", "#A5C0DD", "#FF6969", "#ECC9EE", "#00FFCA"];
在 constructor()
中设置当前这个小球的填充颜色:
this.color = colorArray[Math.floor(Math.random() * colorArray.length)]
在填充之前设定一下颜色:
c.fillStyle = this.color;
到这里,这个简单的项目就结束了,各个颜色的小球会按照随机的速度飘动,鼠标附近的小球会逐渐变大: