H5绘制雪花,JS实现雪花旋转飘落
主要运用canvas+js实现
效果图
步骤:
- 定义雪花: 每片雪花有3条线,每两条线的夹角为60度,每条线长度的一半为snowLength,中点为坐标原点;为了美观,线的长度与宽度的比为一定值;雪花下落自身旋转,给每片雪花一个水平和竖直的随机速度
- 画出一片雪花 : 画布中顶部区域随机一个点,取这一点画一条倾斜随机角度(不要太大,否则后面旋转效果不明显)的线,根据三角函数求出线的两端点的坐标,再依次计算出另外两条线;
- 让画出的线直接旋转是做不了的(反正我没试出来),但可以让下次画出的线在当前展示的线上旋转一个角度,清空当前的线再展示出来,只要线的颜色一样就有旋转效果.
语言太无力,直接上图(请原谅我拙劣的画图功底)
HTML部分:
<canvas height="700px" width="1500px"></canvas>
CSS样式部分:
* {
padding: 0px;
margin: 0px;
list-style: none;
}
:root {
height: 100%;
}
body {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
}
canvas {
border: 1px solid black;
box-sizing: border-box;
background-image:url('./src/timg.jpg');
background-size: cover;
}
JavaScript部分:
准备工作:
var oCanvas = document.getElementsByTagName('canvas')[0];
var ctx = oCanvas.getContext('2d');
// 获取画布宽高
var height = oCanvas.height;
var width = oCanvas.width;
// 初始旋转角度的倍数
var rotateCount = 0;
生成一片随机雪的信息(在画布的位置、大小、长度、x轴和y轴的速度、自身旋转速度)
function randomSnow() {
var left = parseInt(Math.random() * width);
var top = 50;
var snowRotateDeg = parseInt(Math.random() * 10 + 5);
var snowLength = parseInt(Math.random() * 10 + 10) / 2; //该值为雪花每条线的一半
var snowSpeedX = parseInt(Math.random() * 10 + 3);
var snowSpeedY = parseInt(Math.random() * 10 + 2);
// 让画布中一半的雪花向左下角移动
if (Math.random() < 0.5) {
snowSpeedX *= (.5 - Math.random()) * 2;
}
// 将雪花信息返回到外面
return {
left: left,
top: top,
snowRotateDeg: snowRotateDeg,
snowLength: snowLength,
snowSpeedX: snowSpeedX,
snowSpeedY: snowSpeedY
}
}
根据随机生成的雪的信息在画布中画出雪花 (调试时建议用不同的颜色便于观察,看我画的图便于理解)
function drawSnow(deg, snow) {
ctx.beginPath();
// θ角满足正弦规律,即0度时渲染雪花的一条线为竖直方向;θ1表示雪花第一条线与y轴正半轴的夹角
var θ1 = deg % 60 * (Math.PI / 180);
ctx.moveTo(snow.left + snow.snowLength * Math.sin(θ1), snow.top + snow.snowLength * Math.cos(θ1));
ctx.lineTo(snow.left - snow.snowLength * Math.sin(θ1), snow.top - snow.snowLength * Math.cos(θ1));
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
ctx.beginPath();
// θ1表示雪花第二条线与y轴正半轴的夹角
var θ2 = (60 - deg % 60) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.sin(θ2), snow.top + snow.snowLength * Math.cos(θ2));
ctx.lineTo(snow.left + snow.snowLength * Math.sin(θ2), snow.top - snow.snowLength * Math.cos(θ2));
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
ctx.beginPath();
var θ3;
if (deg % 60 + 60 > 90) {
// 随着后续的旋转,deg值会越来越大,也就存在摩除60还比30大的情况,而此时雪花的第三条线就会被挤到随机点的第四象限中,并横跨二、四象限
// 该θ3表示雪花第三条线与x轴正半轴的夹角
θ3 = (deg % 60 - 30) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.cos(θ3), snow.top + snow.snowLength * Math.sin(θ3));
ctx.lineTo(snow.left + snow.snowLength * Math.cos(θ3), snow.top - snow.snowLength * Math.sin(θ3));
} else {
// 该θ3表示雪花第三条线第一次渲染时与x轴正半轴的夹角
θ3 = (30 - deg % 60) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.cos(θ3), snow.top - snow.snowLength * Math.sin(θ3));
ctx.lineTo(snow.left + snow.snowLength * Math.cos(θ3), snow.top + snow.snowLength * Math.sin(θ3));
}
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
}
绘制雪花:
var snowArr = [];
// 每个30毫秒创建一片雪花,
setInterval(function () {
var snow = randomSnow();
snowArr.push(snow);
snowArr = snowArr.filter(function (item) {
//容器保存当前还在画布的雪花
return item.top < height;
})
}, 30)
每隔一定时间清除画布并重绘页面
setInterval(function () {
ctx.clearRect(0, 0, width, height);
snowArr.forEach(function (ele, index) {
drawSnow(ele.snowRotateDeg * rotateCount, ele);
ele.top += ele.snowSpeedY;
ele.left += ele.snowSpeedX;
})
rotateCount++;
}, 50)
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0px;
margin: 0px;
list-style: none;
}
:root {
height: 100%;
}
body {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
}
canvas {
border: 1px solid black;
box-sizing: border-box;
background-image:url('./src/timg.jpg');
background-size: cover;
}
</style>
</head>
<body>
<canvas height="700px" width="1500px"></canvas>
<script>
var oCanvas = document.getElementsByTagName('canvas')[0];
var ctx = oCanvas.getContext('2d');
// 获取画布宽高
var height = oCanvas.height;
var width = oCanvas.width;
// 初始旋转角度的倍数
var rotateCount = 0;
// 生成一片随机雪的信息(在画布的位置、大小、长度、x轴和y轴的速度、自身旋转速度)
function randomSnow() {
var left = parseInt(Math.random() * width);
var top = 50;
var snowRotateDeg = parseInt(Math.random() * 10 + 5);
var snowLength = parseInt(Math.random() * 10 + 10) / 2; //该值为雪花每条线的一半
var snowSpeedX = parseInt(Math.random() * 10 + 3);
var snowSpeedY = parseInt(Math.random() * 10 + 2);
// 让画布中一半的雪花向左下角移动
if (Math.random() < 0.5) {
snowSpeedX *= (.5 - Math.random()) * 2;
}else{
// snowSpeedX /= 5;
}
// 将雪花信息返回到外面
return {
left: left,
top: top,
snowRotateDeg: snowRotateDeg,
snowLength: snowLength,
snowSpeedX: snowSpeedX,
snowSpeedY: snowSpeedY
}
}
// 根据随机生成的雪的信息在画布中画出雪花
function drawSnow(deg, snow) {
ctx.beginPath();
// θ角满足正弦规律,即0度时渲染雪花的一条线为竖直方向;θ1表示雪花第一条线与y轴正半轴的夹角
var θ1 = deg % 60 * (Math.PI / 180);
ctx.moveTo(snow.left + snow.snowLength * Math.sin(θ1), snow.top + snow.snowLength * Math.cos(θ1));
ctx.lineTo(snow.left - snow.snowLength * Math.sin(θ1), snow.top - snow.snowLength * Math.cos(θ1));
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
ctx.beginPath();
// θ1表示雪花第二条线与y轴正半轴的夹角
var θ2 = (60 - deg % 60) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.sin(θ2), snow.top + snow.snowLength * Math.cos(θ2));
ctx.lineTo(snow.left + snow.snowLength * Math.sin(θ2), snow.top - snow.snowLength * Math.cos(θ2));
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
ctx.beginPath();
var θ3;
if (deg % 60 + 60 > 90) {
// 随着后续的旋转,deg值会越来越大,也就存在摩除60还比30大的情况,而此时雪花的第三条线就会被挤到随机点的第四象限中,并横跨二、四象限
// 该θ3表示雪花第三条线与x轴正半轴的夹角
θ3 = (deg % 60 - 30) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.cos(θ3), snow.top + snow.snowLength * Math.sin(θ3));
ctx.lineTo(snow.left + snow.snowLength * Math.cos(θ3), snow.top - snow.snowLength * Math.sin(θ3));
} else {
// 该θ3表示雪花第三条线第一次渲染时与x轴正半轴的夹角
θ3 = (30 - deg % 60) * (Math.PI / 180);
ctx.moveTo(snow.left - snow.snowLength * Math.cos(θ3), snow.top - snow.snowLength * Math.sin(θ3));
ctx.lineTo(snow.left + snow.snowLength * Math.cos(θ3), snow.top + snow.snowLength * Math.sin(θ3));
}
ctx.strokeStyle = "#fff";
ctx.lineCap = 'round';
ctx.lineWidth = snow.snowLength * 4 / 9;
ctx.stroke();
}
// 在画布中展示的雪花
var snowArr = [];
// 每个30毫秒创建一片雪花,
setInterval(function () {
var snow = randomSnow();
snowArr.push(snow);
snowArr = snowArr.filter(function (item) {
// 容器只保存当前还在画布的雪花
return item.top < height;
})
}, 30)
// 每50毫秒重新绘制一次画布
setInterval(function () {
ctx.clearRect(0, 0, width, height);
snowArr.forEach(function (ele, index) {
drawSnow(ele.snowRotateDeg * rotateCount, ele);
ele.top += ele.snowSpeedY;
ele.left += ele.snowSpeedX;
})
rotateCount++;
}, 50)
</script>
</body>
</html>
背景图片