Canvas如何实现樱花雨
一、前言
hi~大家好,我是冰糖。这篇博客是我2023年第一篇博客,在这里先定一个小目标:2023年成为优秀的前端开发者和博主。
那么好,今天我带大家完成一个Canvas的小案例——樱花落。下面是效果图:
看起来还不错,究竟是怎么实现的呢?我为大家分布讲解
二、定义画布
第一步,初始化画布以及上下文ctx
function startDraw() {
// 定义画布
let canvas = document.querySelector("canvas"), ctx;
// 初始化画布的宽高
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
ctx.save() // 保存初始的状态:这一步对后面有用
....
}
三、绘制单个花瓣
这里介绍canvas绘制图片的方法。参考链接
let img = new Image();
img.src = "img/huaban.png";
ctx.drawImage(img,0,0,40,40)
// 图片加载完成时开始画
img.onload = function () {
startDraw()
}
四、定义花瓣类
function huaban(x, y, s, r) {
this.x = x
this.y = y
this.s = s
this.r = r
}
huaban.prototype.draw = function (ctx) {
ctx.translate(this.x, this.y)
ctx.rotate(this.r)
ctx.drawImage(img, 0, 0, 40 * this.s, 40 * this.s)
ctx.restore()
ctx.save()
}
function huabanList() {
this.list = []
}
huabanList.prototype.draw = function (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
this.list.forEach((item) => {
item.draw(ctx)
})
}
huabanList.prototype.push = function (item) {
this.list.push(item)
}
huabanList.prototype.update = function () {
this.list.forEach((item) => {
item.update()
})
}
五、绘制花瓣
通过给定每一个花瓣下所在的坐标(x,y),大小s,以及旋转角度r,绘制出num多少的花瓣
let num = 100
let stop
let newHuabanList = new huabanList()
function startDraw() {
// 定义画布
let canvas = document.querySelector("canvas"), ctx;
// 初始化画布的宽高
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
ctx.save()
// 生成花瓣
for (let i = 0; i < num; i++) {
let randomX, randomY, randomS, randomR, newHuaban;
randomX = getRandom("x");
randomY = getRandom("y");
randomR = getRandom("r");
randomS = getRandom("s");
newHuaban = new huaban(randomX, randomY, randomS, randomR);
newHuabanList.push(newHuaban);
}
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight)
// 画花瓣
newHuabanList.draw(ctx)
// 动画
...待完成
}
function getRandom(option) {
var ret, random;
switch (option) {
case "x":
// x轴随机坐标
ret = Math.random() * window.innerWidth;
break;
case "y":
// y轴随机坐标
ret = Math.random() * window.innerHeight;
break;
case "s":
ret = Math.random();
break;
case "r":
ret = Math.random() * 6;
break;
...
}
return ret;
}
六、添加动画
上一步完成后花瓣分布在屏幕随机位置,但是并没有移动,我们通过requestAnimationFrame()
先clearRect()
清除画布,然后更新每个花瓣位置,再绘制一遍,递归完成(还需要特判花瓣是否超出屏幕)
function startDraw() {
....
// 动画
stop = requestAnimationFrame(function () {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
newHuabanList.update(); // 更新
newHuabanList.draw(ctx); // 绘制
stop = requestAnimationFrame(arguments.callee); // 递归重绘
});
}
// 更新位置
huaban.prototype.update = function () {
this.x = getRandom("fnx")(this.x, this.y)
this.y = getRandom("fny")(this.x, this.y)
this.r = getRandom("fnr")(this.r)
if (
// 判断是否超出边界
this.x > window.innerWidth ||
this.x < 0 ||
this.y > window.innerHeight ||
this.y < 0
) {
// 如果超出边界 则重新生成随机坐标
this.r = getRandom("fnr");
if (Math.random() > 0.4) {
// 如果随机值大于0.4 则重新生成x轴随机坐标
this.x = getRandom("x");
this.y = 0;
this.s = getRandom("s");
this.r = getRandom("r");
} else {
// 如果随机值小于0.4 则重新生成y轴随机坐标
this.x = window.innerWidth;
this.y = getRandom("y");
this.s = getRandom("s");
this.r = getRandom("r");
}
}
}
function getRandom(option) {
var ret, random;
switch (option) {
...
case "fnx":
// x轴随机函数
random = -0.1 + Math.random() * 1;
ret = function (x, y) {
return x + 0.5 * random - 1;
};
break;
case "fny":
// y轴随机函数
random = 0.4 + Math.random() * 0.7;
ret = function (x, y) {
return y + random;
};
break;
case "fnr":
// r轴随机函数
random = Math.random() * 0.02;
ret = function (r) {
return r + random;
};
break;
}
return ret;
}
总结
- 完成此案例,得先知道一个花瓣怎么绘制到多个,到动起来
- 坑:确定画布大小后需要
ctx.save()
对画布初始进行保存,后续更改画布位置,比如:旋转,移动后。都需要对画布还原ctx.restore()
最后,留一个源码地址供大家参考源代码,也感谢各位的star和点赞。感谢您的支持!