1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. <style>
  8. *{
  9. margin: 0;
  10. padding: 0ex;
  11. }
  12. :root,body{
  13. height: 100%;
  14. }
  15. canvas{
  16. background: #000;
  17. /* width: 100%;
  18. height: 100%; */
  19. }
  20. </style>
  21. </head>
  22. <body>
  23. <canvas id="canvas"></canvas>
  24. <script src="./classSnow.js"></script>
  25. </body>
  26. </html>
  1. /**@type{HTMLCanvasElement}*/
  2. const canvas = document.getElementById('canvas');
  3. const ctx = canvas.getContext('2d');
  4. const snowArr = []; // 储存雪花
  5. canvas.width = window.innerWidth;
  6. canvas.height = window.innerHeight;
  7. class Snow {
  8. constructor(x, y, speedx, speedy, scale, rotate, speedR) {
  9. this.x = x; // 雪花显示的x轴位置
  10. this.y = y; // 雪花显示的y轴位置
  11. this.speedx = speedx; // 雪花x轴方向移速
  12. this.speedy = speedy; // 雪花y轴方向移速
  13. this.scale = scale; // 雪花缩放比例
  14. this.rotate = rotate; // 雪花初始旋转角度
  15. this.speedR = speedR; // 雪花每次绘制的旋转角度
  16. }
  17. /**
  18. * 渲染雪花
  19. */
  20. rendom() {
  21. // 保存当前画布的状态
  22. ctx.save()
  23. /**
  24. * 开启新新路径
  25. * 平移画布实现雪花的平移
  26. * 旋转坐标轴实现雪花的的旋转
  27. * 缩放坐标的刻度进行缩放,实现雪花的大小
  28. */
  29. ctx.beginPath()
  30. ctx.translate(this.x, this.y);
  31. ctx.rotate(this.rotate * Math.PI / 180)
  32. ctx.scale(this.scale, this.scale)
  33. // 先话一条x轴的线,作为雪花的基础线
  34. ctx.moveTo(-20, 0)
  35. ctx.lineTo(20, 0)
  36. // 设置线宽线的的颜色以及线的样式
  37. ctx.strokeStyle = "#fff"
  38. ctx.lineWidth = 5;
  39. ctx.lineCap = 'round';
  40. // 计算出 雪花斜线一端在其坐标轴的位置
  41. let disX = Math.sin(30 * Math.PI / 180) * 20;
  42. let dixY = Math.sin(60 * Math.PI / 180) * 20;
  43. // 设置斜线1
  44. ctx.moveTo(disX, -dixY)
  45. ctx.lineTo(-disX, dixY)
  46. // 设置斜线2
  47. ctx.moveTo(-disX, -dixY)
  48. ctx.lineTo(disX, dixY)
  49. // 渲染斜线,并将画布恢复到初始状态
  50. ctx.stroke()
  51. ctx.restore()
  52. }
  53. }
  54. /**
  55. * 随机雪花的属性
  56. */
  57. function randomSnowAttribute() {
  58. /**
  59. * x : x轴位置
  60. * speedX :横向移动速度
  61. * speedY : 竖直方向的移动速度
  62. * rotate : 旋转角度
  63. * scale : 缩放
  64. * speedR : 旋转的角度的大小
  65. */
  66. const x = Math.random() * canvas.width;
  67. const speedX = Math.random() + 1;
  68. const speedY = Math.random() + 5;
  69. const rotate = Math.random() * 60;
  70. const scale = Math.random() + 0.5;
  71. const speedR = Math.random() * 4 + 2;
  72. return {
  73. x,
  74. speedX,
  75. speedY,
  76. rotate,
  77. scale,
  78. speedR
  79. }
  80. }
  81. /**
  82. * 制造雪花
  83. */
  84. function productionSnow() {
  85. for (let i = 0; i < 100; i++) {
  86. // 不定时制造雪花
  87. setTimeout(() => {
  88. let { x, speedX, speedY, rotate, scale, speedR } = randomSnowAttribute();
  89. let snows = new Snow(x, 0, speedX, speedY, scale, rotate, speedR)
  90. snows.rendom()
  91. snowArr.push(snows);
  92. }, Math.random() * 8000);
  93. }
  94. moveSnow()
  95. }
  96. /**
  97. * 移动雪花
  98. */
  99. function moveSnow() {
  100. setInterval(() => {
  101. // 清空画布 每次绘制就清空画布进行重新绘制
  102. ctx.clearRect(0, 0, canvas.width, canvas.height)
  103. // 移动每一个雪花
  104. for (let i = 0; i < snowArr.length; i++) {
  105. let snow = snowArr[i]
  106. snow.x = (snow.x + snow.speedx) % canvas.width;
  107. snow.y = (snow.y + snow.speedy) % canvas.height;
  108. snow.rotate = (snow.rotate + snow.speedR) % 60
  109. snow.rendom()
  110. }
  111. }, 30);
  112. }
  113. /**
  114. * 初始函数
  115. */
  116. function init() {
  117. productionSnow()
  118. }
  119. init()
  120. /**
  121. * 当可视窗口改变时
  122. */
  123. window.onresize = function () {
  124. canvas.width = window.innerWidth;
  125. canvas.height = window.innerHeight;
  126. for (let i = 0; i < snowArr.length; i++) {
  127. setTimeout(() => {
  128. let { x, speedX, speedY, rotate, scale, speedR } = randomSnowAttribute();
  129. snowArr[i].x = x;
  130. }, Math.random() * 8000);
  131. }
  132. }

雪花如何绘制

image.png
雪花是个对称图形,图中橙色的线条就是雪花,从图中可以看圆得直径就是橙色线的长度,你再看图的右上,添加一条绿色的辅助线,刚好成为一个直线三三角形
且锐角为60°与30°的三角形,而我们需要知道线的终点在其的坐标轴的位置,现在一条斜角边为r(也就是蓝色圆得半径),那吗利用三角函数就可以求出另外两条边的长度,得出两条直角边的长度不就得出第一象限中橙色线的终点坐标,sin60° = 对边 / 斜边 对边(也就是y轴坐标) = r * sin60° , x轴同理,由于是对称图形那不就是所在坐标的绝对值相同,只是所在的区间不一样,图形中x轴那条橙色线是事先规定好的,不然哪来的蓝色圆得半径