Canvas --雪花图

雪花图
昨天冬至,又到了白色相簿的季节了,︿( ̄︶ ̄)︿笑,后天就是圣诞了,安安心心做个剩蛋老人吧(ノへ ̄、)。
好了好了,重庆还是没下雪,冷着不下雪,感觉好亏。既然不下,那就只有自己写点雪花了。
canvas这个东西一直是想深入学习的,做出来的东西各种炫酷吊炸天,但是没啥时间,就只会点小东西,大家将就着看吧:
首先既然是用canvas,那么结构就非常简单啦,html什么都不用写,用js创建canvas。
我们把一张背景图放在body上,然后在canvas上进行雪花的绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*{
margin: 0;
padding: 0;
}
html{
width: 100%;
height: 100%;
}
body{
width: 100%;
height: 100%;
background: url("./xh.jpg") center no-repeat;
overflow: hidden;
}

这次就不用es6的写法了,用prototype吧:
首先我们用requestAnimationFrame绘制动画的,先把兼容性搞一下,再创建函数snowFall,设置一下默认参数,maxFlake、flakeSize和fallSpeed,在进行初始化时,可以通过new snowFall()传入这三个参数改变雪花的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//兼容写法
requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) { setTimeout(callback, 1000 / 60); };
function snowFall(snow) {
snow = snow || {};
this.maxFlake = snow.maxFlake || 500; /* 最大片数 */
this.flakeSize = snow.flakeSize || 10; /* 雪花大小 */
this.fallSpeed = snow.fallSpeed || 1; /* 坠落速度 */
this.flakes = []; /* 雪花集合 */
}

然后我们在这个函数上添加一个start方法,调用此方法开始创建画布绘制雪花,出现下雪效果:

1
2
3
4
5
6
7
8
9
// 开始绘制雪花
snowFall.prototype.start = function(){
/* 创建画布 */
snowCanvas.apply(this);
/* 创建雪花形状 */
createFlakes.apply(this);
/* 画雪 */
drawSnow.apply(this);
}

接下来开始创建画布了,建立一个canvas放入body中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建画布
function snowCanvas() {
var snowcanvas = document.createElement("canvas");
snowcanvas.id = "snowfall";
snowcanvas.width = window.innerWidth;
snowcanvas.height = document.body.clientHeight || document.documentElement.clientHeight;
document.getElementsByTagName("body")[0].appendChild(snowcanvas);
this.canvas = snowcanvas;
this.ctx = snowcanvas.getContext("2d");
/* 窗口大小改变的处理 */
window.onresize = function () {
snowcanvas.width = window.innerWidth;
snowcanvas.height = window.innerHeight;
}
}

接着创建雪花的形状,这里循环一下要绘制的雪花,用一个雪运动对象设置每个雪花的参数,然后将雪花放入雪花集合中:

1
2
3
4
5
6
//创建雪花对象
function createFlakes() {
for (var i = 0; i < this.maxFlake; i++) {
this.flakes.push(new flakeMove(this.canvas.width, this.canvas.height, this.flakeSize, this.fallSpeed))
}
}

雪花运动对象,用于生成每个雪花的一系列参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//雪运动对象
function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) {
this.canvasw = canvasWidth;
this.canvash = canvasHeight;
this.x = Math.floor(Math.random() * canvasWidth); /* x坐标 */
this.y = Math.floor(Math.random() * canvasHeight); /* y坐标 */
this.size = Math.random() * flakeSize + 2; /* 形状 */
this.maxSize = flakeSize; /* 最大形状 */
this.speed = Math.random() * 1 + fallSpeed; /* 坠落速度 */
this.fallSpeed = fallSpeed; /* 坠落速度 */
this.velY = this.speed; /* Y方向速度 */
this.velX = 0; /* X方向速度 */
this.stepSize = Math.random() / 100; /* 步长 */
}

在雪的运动对象上添加一个render方法,用于生成雪的形状

1
2
3
4
5
6
7
8
9
10
11
12
13
// 渲染雪花-随机形状(此处可修改雪花颜色!!!)
flakeMove.prototype.render = function (ctx) {
var snowFlake = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
snowFlake.addColorStop(0, "rgba(255, 255, 255, 0.9)"); /* 此处是雪花颜色,默认是白色 */
snowFlake.addColorStop(.5, "rgba(255, 255, 255, 0.5)");
snowFlake.addColorStop(1, "rgba(255, 255, 255, 0)");
ctx.save();
ctx.fillStyle = snowFlake;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
};

在雪的运动对象上添加一个雪花的运动方法,根据传入的参数不同,来让雪向左向右运动,当雪花飞到了边界后,调用reset方法重置这个雪花的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
flakeMove.prototype.update = function (n) {
var x = this.x,
y = this.y;
if (n) {
this.velX += this.stepSize;
} else {
this.velX -= this.stepSize;
}
this.y += this.velY;
this.x += this.velX;
/* 飞出边界的处理 */
if (this.x >= this.canvasw || this.x <= 0 || this.y >= this.canvaswh || this.y <= 0) {
this.reset(this.canvasw, this.canvash)
}
};
/* 飞出边界-放置最顶端继续坠落 */
flakeMove.prototype.reset = function (width, height) {
this.x = Math.floor(Math.random() * width);
this.y = 0;
this.size = Math.random() * this.maxSize + 2;
this.speed = Math.random() * 1 + this.fallSpeed;
this.velY = this.speed;
this.velX = 0;
};

雪花对象设置完毕后,我们开始画雪了,每次绘制前我们都要清空一下画布,然后根据雪花的集合进行遍历,调用函数让一部分雪花向左飘,一部分向右飘,然后用requestAnimationFrame继续调用画雪的函数,一帧一帧绘制雪花:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 画雪 */
function drawSnow() {
/* 清空雪花 */
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
for (var e = 0; e < this.maxFlake; e++) {
if (e % 3 == 0) {
this.flakes[e].update(0);
this.flakes[e].render(this.ctx);
} else {
this.flakes[e].update(1);
this.flakes[e].render(this.ctx);
};
}
/* 一帧一帧的画 */
this.loop = requestAnimationFrame(function () {
drawSnow.apply(this);
}.bind(this));
}

最后我们 new snowFall(),最多绘制100个雪花,然后调用start方法,雪花就开始飞舞了:

1
2
3
4
5
/* 调用及控制方法 */
var snow = new snowFall({
maxFlake: 100
});
snow.start();

效果展示:

本文代码地址:链接