<!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>
*{
margin: 0;
padding: 0ex;
}
:root,body{
height: 100%;
}
canvas{
background: #000;
/* width: 100%;
height: 100%; */
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="./classSnow.js"></script>
</body>
</html>
/**@type{HTMLCanvasElement}*/
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const snowArr = []; // 储存雪花
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
class Snow {
constructor(x, y, speedx, speedy, scale, rotate, speedR) {
this.x = x; // 雪花显示的x轴位置
this.y = y; // 雪花显示的y轴位置
this.speedx = speedx; // 雪花x轴方向移速
this.speedy = speedy; // 雪花y轴方向移速
this.scale = scale; // 雪花缩放比例
this.rotate = rotate; // 雪花初始旋转角度
this.speedR = speedR; // 雪花每次绘制的旋转角度
}
/**
* 渲染雪花
*/
rendom() {
// 保存当前画布的状态
ctx.save()
/**
* 开启新新路径
* 平移画布实现雪花的平移
* 旋转坐标轴实现雪花的的旋转
* 缩放坐标的刻度进行缩放,实现雪花的大小
*/
ctx.beginPath()
ctx.translate(this.x, this.y);
ctx.rotate(this.rotate * Math.PI / 180)
ctx.scale(this.scale, this.scale)
// 先话一条x轴的线,作为雪花的基础线
ctx.moveTo(-20, 0)
ctx.lineTo(20, 0)
// 设置线宽线的的颜色以及线的样式
ctx.strokeStyle = "#fff"
ctx.lineWidth = 5;
ctx.lineCap = 'round';
// 计算出 雪花斜线一端在其坐标轴的位置
let disX = Math.sin(30 * Math.PI / 180) * 20;
let dixY = Math.sin(60 * Math.PI / 180) * 20;
// 设置斜线1
ctx.moveTo(disX, -dixY)
ctx.lineTo(-disX, dixY)
// 设置斜线2
ctx.moveTo(-disX, -dixY)
ctx.lineTo(disX, dixY)
// 渲染斜线,并将画布恢复到初始状态
ctx.stroke()
ctx.restore()
}
}
/**
* 随机雪花的属性
*/
function randomSnowAttribute() {
/**
* x : x轴位置
* speedX :横向移动速度
* speedY : 竖直方向的移动速度
* rotate : 旋转角度
* scale : 缩放
* speedR : 旋转的角度的大小
*/
const x = Math.random() * canvas.width;
const speedX = Math.random() + 1;
const speedY = Math.random() + 5;
const rotate = Math.random() * 60;
const scale = Math.random() + 0.5;
const speedR = Math.random() * 4 + 2;
return {
x,
speedX,
speedY,
rotate,
scale,
speedR
}
}
/**
* 制造雪花
*/
function productionSnow() {
for (let i = 0; i < 100; i++) {
// 不定时制造雪花
setTimeout(() => {
let { x, speedX, speedY, rotate, scale, speedR } = randomSnowAttribute();
let snows = new Snow(x, 0, speedX, speedY, scale, rotate, speedR)
snows.rendom()
snowArr.push(snows);
}, Math.random() * 8000);
}
moveSnow()
}
/**
* 移动雪花
*/
function moveSnow() {
setInterval(() => {
// 清空画布 每次绘制就清空画布进行重新绘制
ctx.clearRect(0, 0, canvas.width, canvas.height)
// 移动每一个雪花
for (let i = 0; i < snowArr.length; i++) {
let snow = snowArr[i]
snow.x = (snow.x + snow.speedx) % canvas.width;
snow.y = (snow.y + snow.speedy) % canvas.height;
snow.rotate = (snow.rotate + snow.speedR) % 60
snow.rendom()
}
}, 30);
}
/**
* 初始函数
*/
function init() {
productionSnow()
}
init()
/**
* 当可视窗口改变时
*/
window.onresize = function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
for (let i = 0; i < snowArr.length; i++) {
setTimeout(() => {
let { x, speedX, speedY, rotate, scale, speedR } = randomSnowAttribute();
snowArr[i].x = x;
}, Math.random() * 8000);
}
}
雪花如何绘制
雪花是个对称图形,图中橙色的线条就是雪花,从图中可以看圆得直径就是橙色线的长度,你再看图的右上,添加一条绿色的辅助线,刚好成为一个直线三三角形
且锐角为60°与30°的三角形,而我们需要知道线的终点在其的坐标轴的位置,现在一条斜角边为r(也就是蓝色圆得半径),那吗利用三角函数就可以求出另外两条边的长度,得出两条直角边的长度不就得出第一象限中橙色线的终点坐标,sin60° = 对边 / 斜边 对边(也就是y轴坐标) = r * sin60° , x轴同理,由于是对称图形那不就是所在坐标的绝对值相同,只是所在的区间不一样,图形中x轴那条橙色线是事先规定好的,不然哪来的蓝色圆得半径