HTML如何做雪花效果_HTML下雪飘雪动画效果实现【附代码】

作者:袖梨 2026-06-27
用 canvas 实现高性能雪花动画需控制数量(150–300个)、适配设备像素比(缩放 canvas 并调用 ctx.scale)、优化随机参数分布、监听 visibilitychange 暂停后台动画,并在 resize 时重置 canvas 尺寸和缩放。

canvas 实现高性能雪花动画,别碰 div + CSS animation

纯 CSS 做几百个 div 模拟雪花,页面直接卡死,尤其在低配设备或 Safari 上掉帧严重。真正可用的方案是用 canvas 逐帧绘制——它不创建 DOM 节点,内存占用低,60fps 稳定。关键不是“怎么动”,而是“怎么控制数量和生命周期”。

实操建议:

立即学习“前端免费学习笔记(深入)”;

  • 初始化时生成 150–300 个雪花对象(太少没氛围,太多吃性能),每个含 xysizespeedopacity 属性
  • 每帧用 ctx.clearRect(0, 0, canvas.width, canvas.height) 清屏,再循环绘制:用 ctx.beginPath() + ctx.arc() 画圆点,或用 ctx.fillText('❄', x, y) 渲染符号(字体加载需兜底)
  • 下落逻辑:y += speed;超出底部后重置到顶部,x 加随机偏移模拟风向飘动
  • 避免用 requestAnimationFrame 外层套 setTimeout,会丢帧;直接裸调即可

requestAnimationFrame 必须和设备像素比对齐,否则模糊或抖动

Canvas 在高清屏上默认按 CSS 像素渲染,雪花边缘发虚、移动时出现“跳帧感”。根本原因是 canvas 的 width/height 属性值没随 window.devicePixelRatio 缩放。

实操建议:

立即学习“前端免费学习笔记(深入)”;

  • 获取真实渲染尺寸:const dpr = window.devicePixelRatio || 1
  • 设置 canvas 标签属性:canvas.width = canvas.offsetWidth * dprcanvas.height = canvas.offsetHeight * dpr
  • 然后用 ctx.scale(dpr, dpr) 让绘图坐标系回归 CSS 像素单位,否则所有 x/y 都要手动乘 dpr
  • 注意:resize 事件中必须重新计算并重设 width/height,不能只改 CSS 样式

雪花大小、透明度、速度得用随机分布,但不能真“完全随机”

全用 Math.random() 会导致大量雪花挤在中间、边缘稀疏,或忽快忽慢破坏视觉节奏。人眼对“自然下落”的判断其实很敏感。

实操建议:

立即学习“前端免费学习笔记(深入)”;

  • 大小用 Math.random() * 2 + 1(1–3px),避免出现 >4px 的突兀大点
  • 透明度推荐 Math.random() * 0.5 + 0.2(0.2–0.7),太透看不见,太实像雨滴
  • 垂直速度用 Math.random() * 1.5 + 0.8(0.8–2.3px/frame),再叠加一个缓慢的 sin(x * 0.001) 模拟微风摆动
  • 水平偏移量控制在 ±0.3px/frame 内,否则雪花像被狂风吹跑,失去“飘”的质感

移动端必须监听 visibilitychange,否则切后台还狂耗电

手机切到其他 App 后,requestAnimationFrame 在部分 Android 浏览器里仍持续触发,CPU 温度飙升,用户秒关网页。这不是 bug,是规范允许行为。

实操建议:

立即学习“前端免费学习笔记(深入)”;

  • 加监听:document.addEventListener('visibilitychange', () => { if (document.hidden) cancelAnimationFrame(animId); })
  • 切回前台时重新启动动画,别用 setInterval 补帧——时间差会导致雪花位置突变
  • 低端 Android(如 Chrome 80 以下)可能不触发 visibilitychange,可加兜底:检测连续 5 秒无用户交互(mousemove/touchstart)后暂停

最易被忽略的是 canvas 尺寸重置逻辑——很多人只写一次初始化,窗口缩放后雪花被拉伸变形,还以为是动画代码有问题。实际只要漏了 resize 时的 width/height 重赋值和 ctx.scale 重设,整个效果就废了一半。

相关文章

精彩推荐