<!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;// 设置斜线1ctx.moveTo(disX, -dixY)ctx.lineTo(-disX, dixY)// 设置斜线2ctx.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) % 60snow.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轴那条橙色线是事先规定好的,不然哪来的蓝色圆得半径
