如何用打造网页天气可视化效果?

摘要:搜索"网页天气效果",你大概率会找到两类东西:一类是纯 CSS 写的下雨动画,十几行代码,@keyframes 让 div 从上往下飘;另一类是"调用天气 API
搜索"网页天气效果",你大概率会找到两类东西:一类是纯 CSS 写的下雨动画,十几行代码,@keyframes 让 div 从上往下飘;另一类是"调用天气 API 展示温度"的教程,跟视觉效果没半点关系。 真正意义上的"沉浸式天气可视化"——雨滴打到界面元素上溅射、雪花堆积在导航栏、镜头光斑随太阳位置偏移——这类东西,中文社区几乎是空白。英文社区也好不到哪去,CodePen 上那些酷炫的效果基本不开源,或者用了 WebGL 库,拿过来改也费劲。 所以我干脆自己做了一个。 项目用 Next.js + Canvas 2D + CSS 实现,支持晴天、雨天、雪天、阴天、雾天五种天气,每种都有一套可以实时调节的参数面板——雨量、风力、温度、雷暴概率、能见度,拖滑块即时生效。还接了 Open-Meteo 的免费 API,开启自动模式后会读取你的浏览器定位,展示你当前位置的真实天气。 在线体验:https://weather.anhejin.cn 开源地址:https://github.com/greywen/web-weather Canvas、CSS、WebGL,选哪个 这是个经常被过度讨论的问题。 我的答案是:Canvas 2D 做粒子效果,CSS 做雾气和云层,WebGL 完全没用到。 不是说 WebGL 不好,是杀鸡用牛刀。雨滴最多也就两三百个粒子,雪花上限我设了五百个,Canvas 2D 跑起来 60fps 没什么压力。WebGL 的优势在几万个粒子以上,引入 Three.js 或者 raw WebGL 反而增加了整个项目的复杂度,调试也麻烦。 CSS 适合处理"大范围、有纹理感"的东西。雾气那层我用 CSS 做了烟雾纹理飘动,配合 backdrop-filter: blur() 做整体模糊感,效果比 Canvas 画出来的要自然很多。云层也是纯 CSS 动画,用 Web Animations API 做速度控制,这样可以根据风力参数实时改变云的移动速度,不用每帧重新计算。 Canvas 和 CSS 混用,有一个麻烦点:层叠顺序。Canvas 是一个 DOM 元素,CSS overlay 是另外几个 div,你得管好谁在谁上面,不然会出现 fog blur 把 Canvas 的 rain 也模糊掉这种情况。我在 WeatherProvider 里专门处理了这个,用 z-index 把各层分开,Canvas 在底,CSS fog 在上,控制面板最顶。 雨天:从一条线到溅射粒子 最早的版本,雨滴就是一条线——ctx.moveTo 到 ctx.lineTo,简单粗暴。后来改成了梯形,上窄下宽,模拟真实水滴下落时被空气拉扁的形态: // 梯形雨滴:上窄下宽consttopHalfWidth =0.3;constbottomHalfWidth =1.2;ctx.moveTo(tx - topHalfWidth, ty);ctx.lineTo(tx + topHalfWidth, ty);ctx.lineTo(bx + bottomHalfWidth, by); // bx = tx + windOffsetctx.lineTo(bx - bottomHalfWidth, by); 风力通过 windOffset 让梯形底部偏移,雨滴看起来是斜着落的,比单纯的线条有质感多了。 数据结构上,雨滴没有用 class,而是用了 SoA(Struct of Arrays)布局——所有 x 坐标放一个 Float32Array,所有 y 坐标放另一个,速度、长度、透明度也各自一个数组。这样做的好处是内存连续访问,CPU 缓存命中率高,几百个粒子循环更新的时候比逐个对象访问快不少。绘制的时候按透明度分三档批量 ctx.fill(),一次 beginPath 画一批,减少 draw call。 溅射粒子也做了类似的处理——用一个固定大小的 Float32Array 对象池,移除死亡粒子时用 swap-and-pop(把末尾元素换到当前位置),O(1) 移除,不用 splice。画的时候统一一个 fillStyle,所有溅射点一次 fill 搞定。这个效果加进去之后整个场景的"物理感"一下子就上来了。 雷暴是另一个让我花了不少时间的东西。闪电要有分叉,不然看起来就是一条直线,完全没感觉。
阅读全文