☰
前端下雨效果实战:Canvas粒子系统从选型到性能优化
2026/10/13 11:11:21 网站建设 项目流程

你别笑,我每次在搜索引擎里敲下“前端 下雨 效果”这几个字的时候,心里都挺复杂。需求描述起来就一句话,“页面上下下雨”,但搜出来的结果能把你从 CSS 基础拉到图形学入门,中间还掺杂着各种几年前的 demo 和“复制这段代码即可”的残缺片段。这行当就是这样,大部分时候你以为是找答案,实际是在做信息考古。这篇就当作我给自己留的一份笔记,也顺便给刚入坑前端、想给页面加点氛围感的同行踩出一条相对靠谱的路。我会从需求拆解讲到技术选型,再给出一套我用 Canvas 实现的完整方案、参数调优思路,以及那些文档里不会写的坑。

1. 为什么“页面下雨”会变成一门玄学

先说个结论:真正玄的不是技术,是需求本身。“下雨效果”这四个字的信息量,跟“做个按钮”完全不在一个量级。按钮你问清楚颜色、圆角、文案就可以了,下雨你得先弄清楚用户脑子里想的是哪一种雨。

1.1 需求本身埋着雷

我遇到过至少三种理解偏差很大的场景。第一种是背景氛围雨,就是页面首页、落地页这种地方,用户想看的是淡淡的疏离感,雨丝细得几乎察觉不到,更像一层动态纹理。第二种是玻璃雨,常在 H5 活动页里出现,雨滴打在透明玻璃上,要能聚集、滑落、模糊后面的元素,这个复杂度直接往上翻几倍。第三种是沉浸式暴雨,配合音效、闪电动画,用来讲故事或者做游戏开场。

你把这三种需求都搜成“下雨效果”,搜索引擎给你的自然是五花八门。所以拿到需求,第一件事不是打开编辑器,而是反问一句:你要的是哪种雨?这个雨是背景装饰,还是核心视觉?是点缀互动,还是必须吸引用户停留?需求界定的颗粒度,决定了后面所有工作的方向。

另外一个很隐蔽的问题,是“简单”这个词的语义漂移。提需求的人说“简简单单下个雨”,他心里默认的是浏览器自己就会下雨,像 HLSL 里调个函数一样。但前端的现实是:你用 CSS 动画搞点雨丝不难,要做到雨滴连续、随机、不重复、不掉帧、可交互,难度是阶梯式上升的。所以“玄学感”的根源,在于预期和实现成本之间几乎没有共识。

1.2 搜索结果的三大流派

以我的搜索体验,主流方案分三拨。第一拨是“素材流”,告诉你去找一张带透明通道的雨丝 PNG,然后平铺加位移。这是最快见效的做法,但问题也最明显:素材分辨率限制了清晰度,循环重复感很强,而且一旦页面宽度超过素材尺寸,穿帮是必然的。第二拨是“动画流”,用 CSS 的 keyframes 配合多条雨丝 div 做位移动画,适合做装饰性的细小雨丝,但粒子数量一多,DOM 节点爆炸,性能就难看了。第三拨是“画血流”,用 Canvas 或者 WebGL 逐帧绘制雨滴位置。这是正路,也是这篇文章的主题。

讽刺的是,搜索结果里第三拨方案往往是最好用的,但信息密度也最高,新手在“概率密度”“随机分布”“对象池”这些词面前很容易退却,最后又绕回素材流,陷入“玄学循环”。其实画血流的核心思想极简单:把屏幕当作坐标系,每帧移动一堆点的位置,仅此而已。

1.3 雨滴看起来到底像什么

这里有一个反直觉的视觉细节,值得单独拎出来说:雨滴在屏幕上不是一个圆点,而是一条线。原因也很简单,人眼看到雨滴时,它正在高速下落,视觉暂留效应让它拖出轨迹。所以你在 Canvas 里画雨,正确的做法是draw一条短线段,起点和终点分别代表雨滴头部和尾迹,而不是画一个实心圆。这个细节直接影响观感,我见过不少人把雨滴画成密密麻麻的小圆点,结果远看像下雪、像虫群,就是不像雨。

搞清楚“雨滴=高速运动的短线段”这个模型,后面的物理模拟就有方向了。每条雨丝就是一个粒子,你需要为它维护坐标、下落速度、线段长度,以及一个随机数种子来避免所有雨丝同步运动。把这些想明白之后,再回看那些搜索结果,你会发现玄学感瞬间消失,剩下的只是技术选型和性能权衡。

2. 技术选型:五种方案的真实对比

既然需求千差万别,技术选型就不能只看“能不能实现”,还得看“维护成本”和“容错空间”。我把常见方案按从简到繁排了个序,附带我实际使用后的体感,这样可以帮你快速找到自己需要的档位。

2.1 CSS 模拟:适合有节制的氛围场景

CSS 方案最典型的做法是用一个或多个半透明渐变背景,叠加重复背景动画,制造雨丝下落的视觉。另一种常见做法是生成十几条 div 细线,每条设定不同的 animation-delay 和 animation-duration,让它们循环下落。

这种方案的价值在于零依赖、代码量少,也不会增加 Canvas 那一层渲染负担。缺点是颗粒度太粗,你很难控制雨滴的随机分布,动画一旦循环就容易看出规律。我的经验是它只适合“锦上添花”的场景,比如一个纯展示的落地页,用户扫一眼就走,不会盯住屏幕看两分钟。除此之外,如果设计要求雨滴碰到页面底部还有溅射效果,CSS 基本就得靠额外 hack,成本反而上去了。

2.2 Canvas 2D:最均衡的折中方案

如果你要的是“看起来是真的在下雨”,同时希望代码量可控,Canvas 2D 是性价比最高的选择。Canvas 本质上是一块画布,你每帧用 JS 清空再重绘,雨滴只是画布上的线段集合。这种方式最大的好处是:雨滴数量、速度、方向、透明度、颜色、长度全部是变量,你可以随时调参,所见即所得。

性能上,Canvas 2D 的绘制能力足以支撑几百根雨丝在 60fps 下流畅运行,这对绝大多数页面场景都够用了。配合 requestAnimationFrame 做帧循环,再加上设备像素比适配,效果可以非常顺滑。维护成本也低,核心代码不到 200 行就能跑起来,后面加风、加闪电、加水花都有扩展空间。如果你没有明确的高性能渲染诉求,我建议就用这个,不要在选型上耗太久。

2.3 SVG:细节控的偏好

SVG 也能实现雨滴动画,一些开发者喜欢用它,因为 SVG 元素可以单独设置 CSS 类、绑定事件,语义化程度比 Canvas 高。你可以用<line>或者<path>声明一条雨丝,然后用 SMIL 动画或者 CSS 控制位移。

但我实际用下来,SVG 在“大量粒子”场景下并不占优。每个雨滴对应一个真实 DOM 节点,几十个还能接受,上百个之后,即便 SVG 是矢量渲染,DOM 操作和布局开销也会让性能变得不稳定。而且不同浏览器的 SVG 动画性能差异较大,移动端更容易卡。所以我把 SVG 定位为“细节控的偏好”,适合雨滴数量少、需要独立交互的场景,比如每个雨滴点击后有反应,而不是大规模雨幕。

2.4 WebGL:重度效果的终局

如果你追求电影级的雨效——景深、粒子数量过万、雨滴折射、玻璃水滴物理模拟,那 Canvas 2D 就有点勉强了,要上 WebGL。WebGL 的粒子系统是在 GPU 上跑的,几万根雨丝同时渲染几乎是家常便饭。

问题在于上手门槛陡增,你要理解着色器、缓冲区、纹理等一系列概念。如果你只是想在页面上点缀一点逼真的细雨,用 WebGL 属于“杀鸡用牛刀”,开发周期会明显拉长,后续调参也需要一定的图形学基础。我的建议是,除非你的项目已经有成熟的 WebGL 渲染架构,或者明确需要和 3D 场景联动,否则别直接上,先评估一下 Canvas 2D 的上限是不是真的不够。

2.5 背景视频:最省事的偷懒法

还有一种方案是直接找一段下雨的视频或 GIF 当背景,设置循环播放和透明度。效果嘛,确实很真实,因为它就是真下雨的画面。但它的问题同样致命:视频文件体积大,加载慢,版权归属难确认,透明的视频素材不好找。如果你又不想露馅,页面上其他元素还得往视频里的透视关系、色调上靠,限制太多了。

我做项目时,只在一种情况下会用视频:客户对效果要求极高、发布时间紧张,且能找到高质量免版权素材。平时我更愿意用代码实现,因为代码实现出来的雨幕是可以跟页面交互联动的,比如鼠标经过雨滴扩散、滚动页面雨速变化,这是视频素材做不到的。

2.6 一个简单的选型对照表

方案视觉效果上限开发成本性能成本适用场景
CSS 背景动画低最低低静态落地页氛围点缀
Canvas 2D中高中中大部分页面雨幕效果
SVG 动画中中中偏高少量雨滴加交互
WebGL 粒子高高低(GPU)重度视觉、3D 场景
视频/GIF 背景高(真实)最低高(加载)临时方案、高保真素材

看完表格你应该有谱了,接下来我用 Canvas 2D 方案给你完整拆一遍,包括每一步的参数计算逻辑和优化细节。

3. Canvas 实现:我的实操过程拆解

方案定下来,剩下的就是动手。以下从零开始搭一个 Canvas 雨幕,代码我会分段解释,确保你不是直接复制粘贴,而是真能理解每一行的作用。

3.1 先搭一个最基础的渲染循环

Canvas 动画的第一件事是拿到画布上下文,然后定义一个主循环。主循环里每帧做三件事:清空画布、更新粒子状态、重新绘制。用requestAnimationFrame而不是setInterval,前者会跟随屏幕刷新率,避免掉帧时任务堆积,还能在页面切后台时自动暂停,省电省 CPU。

const canvas = document.getElementById('rain-canvas'); const ctx = canvas.getContext('2d'); let width, height; function resize() { width = canvas.width = window.innerWidth; height = canvas.height = window.innerHeight; } resize(); window.addEventListener('resize', resize); function frame() { // 清空画布 ctx.clearRect(0, 0, width, height); // 更新与绘制 updateAndDraw(); requestAnimationFrame(frame); } requestAnimationFrame(frame);

这段代码有个细节需要注意:canvas.width直接赋值成window.innerWidth会自动清空画布,所以要用clearRect也行,但宽度重置时更省事。另一个细节是,如果你要支持高分屏,就得把物理像素和逻辑像素分开:

const dpr = window.devicePixelRatio || 1; canvas.width = window.innerWidth * dpr; canvas.height = window.innerHeight * dpr; canvas.style.width = window.innerWidth + 'px'; canvas.style.height = window.innerHeight + 'px'; ctx.scale(dpr, dpr);

不处理 DPR 的话,雨丝在 Retina 屏幕上会发虚,这个后面视觉效果差距非常明显,属于必须做的适配。

3.2 雨滴的物理模型与坐标系

回到之前那个模型:每一个雨滴就是一条短线段。在线段落地之前,它沿 Y 轴方向匀速运动,沿 X 轴甚至可以有风带来的偏移。

一个雨滴粒子的数据结构大致是这么几项:

class RainDrop { constructor() { this.reset(); } reset() { this.x = Math.random() * width; this.y = Math.random() * height - height; // 从屏幕上方外开始 this.speed = 3 + Math.random() * 7; // 下落速度 this.length = 8 + Math.random() * 12; // 雨丝长度 this.opacity = 0.2 + Math.random() * 0.4; // 透明度 } }

这里有几个参数很关键。speed代表每帧下落的像素偏移,范围放在 3 到 10 之间比较合适,太慢像飘雪,太快又容易让人眼睛疲劳。length代表雨丝的长度,8 到 20 像素在普通屏幕上刚好能体现出“线”而不是“点”的感觉,太长反而失真。opacity设置在 0.2 到 0.6 之间,低于 0.2 几乎看不见,高于 0.6 会显得雨幕很脏。

有了这些数据以后,每帧更新就是简单地在 Y 坐标上累加speed,如果超过屏幕高度就重置回屏幕顶部。当y > height时,调用reset()把这条雨丝挪到上方重新开始。这就是最朴素的粒子循环。

3.3 从一颗雨滴到整个雨幕

单颗雨滴撑不起氛围,我们需要同时维护一批雨滴。在这里,代码量和视觉效果的平衡点是一个需要实践的问题。

const drops = []; const MAX_DROPS = 300; for (let i = 0; i < MAX_DROPS; i++) { drops.push(new RainDrop()); } function updateAndDraw() { ctx.clearRect(0, 0, width, height); for (const drop of drops) { drop.y += drop.speed; if (drop.y > height) { drop.reset(); } ctx.strokeStyle = `rgba(180, 210, 255, ${drop.opacity})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(drop.x, drop.y); ctx.lineTo(drop.x, drop.y - drop.length); ctx.stroke(); } }

我把最大粒子数量定在 300,这个数字不是拍脑袋。在我自己的项目里,200 到 400 是一个甜点区:低于 200 显得雨量稀疏,氛围感不足;高于 400,Canvas 2D 的stroke操作开始产生可见的 CPU 压力,帧率会掉到 50 以下。你可以在调试面板里实时调整这个值,观察帧率曲线的变化。注意雨滴的初始y是负值,也就是在屏幕上方之外,这样第一帧画面就是完整的雨幕,而不是从上往下“播种”的过程。

3.4 参数标定:密度、速度、透明度、风偏

“像不像下雨”完全是参数堆出来的。我整理了一份自己常用的参数表,给不同天气状态做推荐值,按需微调即可。

参数小雨中雨暴雨
粒子数量120200400
速度范围2-54-98-15
雨丝长度6-128-2014-28
透明度范围0.2-0.40.3-0.50.4-0.7
风偏移量00.5-1.51-3

风偏移量这个概念,简单说就是每帧让雨滴的x坐标额外移动一点。比如风从左往右吹,就给drop.x加上windSpeed。风太强时雨丝会变得明显倾斜,视觉上反而更真实,因为屏幕上的雨本来就受环境风影响。

drop.x += windSpeed; if (drop.x > width + 20) { drop.x = -20; }

注意左右边界要留出冗余,避免雨滴直接消失在同一侧,产生突兀感。我通常在width外扩 20 像素再判断,这样雨丝是“走出屏幕外”而不是“被屏幕裁掉”。

3.5 加入风、溅射、湿润感

基础雨幕成型后,真正让效果从“能用”变成“好看”的,是几个加在细节上的小动作。

第一个小动作是分层。传统的平铺雨幕有一种“纸片感”,因为所有雨滴都落在同一个平面上。改进方法是定义两层雨滴:近处的雨丝更长、更透明、速度更快,远处的雨丝更短、更淡、速度更慢。近景和远景叠加之后,透视感和空气感就出来了。

第二个小动作是地面溅射。当雨滴到达屏幕底部时,不要让它直接消失,而是画一个极小的半透明圆弧模拟水花。这个效果可以用一个独立的水花数组维护,生命期很短,大概 3 到 5 帧就会淡出。

第三个小动作是“雨落在屏幕上的湿润感”。严格模拟玻璃上的水渍需要复杂的物理模型,但我们可以用一条从上到下逐渐加深的渐变叠加层来模拟屏幕潮湿。具体做法是绘制一个垂直方向的半透明渐变,顶部深、底部浅,混合模式用multiply或者overlay。代码量不大,但整体质感提升非常明显。

3.6 完整可运行代码

把上面的逻辑合并,就是一个可以直接存为 HTML 文件打开看效果的版本:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Canvas 雨幕效果</title> <style> body { margin: 0; overflow: hidden; background: #0a0e1a; } canvas { display: block; } </style> </head> <body> <canvas id="rain-canvas"></canvas> <script> const canvas = document.getElementById('rain-canvas'); const ctx = canvas.getContext('2d'); let width, height; const dpr = window.devicePixelRatio || 1; function resize() { width = window.innerWidth; height = window.innerHeight; canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } resize(); window.addEventListener('resize', resize); class RainDrop { constructor() { this.reset(); } reset() { this.x = Math.random() * width; this.y = Math.random() * height - height; this.speed = 3 + Math.random() * 7; this.length = 8 + Math.random() * 12; this.opacity = 0.2 + Math.random() * 0.4; this.windOffset = Math.random() * 1.2; } } const drops = []; const MAX_DROPS = 300; for (let i = 0; i < MAX_DROPS; i++) { drops.push(new RainDrop()); } function frame() { ctx.clearRect(0, 0, width, height); for (const drop of drops) { drop.y += drop.speed; drop.x += drop.windOffset * 0.5; if (drop.y > height + 20) { drop.reset(); drop.y = -10; } if (drop.x > width + 20) { drop.x = -20; } ctx.strokeStyle = `rgba(180, 210, 255, ${drop.opacity})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(drop.x, drop.y); ctx.lineTo(drop.x - drop.windOffset * 2, drop.y - drop.length); ctx.stroke(); } requestAnimationFrame(frame); } requestAnimationFrame(frame); </script> </body> </html>

这个版本已经可以直接跑出自然的多雨效果。如果你想让它更精致,下一步就该认真考虑性能问题,尤其是移动端和低配电脑。

4. 性能优化与移动端适配

写特效有句老话:效果好不好,要看低端机上卡不卡。Canvas 雨幕的瓶颈集中在粒子数量和绘制 API 的调用频次上,所以优化思路基本围绕这两点展开。

4.1 把粒子数压到“看不见上限”

很多初学者一上来就把粒子数拉到 1000,看桌面电脑全屏 60fps,以为没问题。一上手机、一开 devtools 的 CPU 降频模拟,立刻暴露。Canvas 2D 的stroke是一个开销很高的操作,每帧调用 1000 次stroke()和只调用 300 次差别的帧耗时,往往是几倍的关系。

我的实践是:先把粒子数压到 200,然后按“你转动屏幕或移动页面时看不出破绽”为标准往上加。如果加到 400 已经流畅,就停在 400;如果需要性能余量,就退回 300。另外,可以把雨丝长度和透明度做成随机分布,这样即使粒子数只有 180,视觉上也不会显得稀疏,因为每个粒子面积和明暗都不同。

还有一个容易忽略的点:移动端页面滚动的场景,Canvas 雨幕如果固定在视口内,那雨滴只跟着屏幕走;如果放在全屏背景容器里,那么滚动时 canvas 内容必须正确跟随。我遇到过的坑是,把 canvas 设为position: fixed,结果页面滚动时雨还在原地,这是理所当然的,因为 fixed 定位本来就不滚。需要根据产品意图决定:雨到底是背景还是装饰层。

4.2 用对象池代替频繁创建粒子

雨滴需要持续复用,不要每帧 new 一个。下面的代码更合理,直接预先创建一批粒子,用循环轮流复位:

const drops = new Array(MAX_DROPS).fill(0).map(() => new RainDrop());

当雨滴超出屏幕,调用reset()重置状态,而不是splice()后重新 push。频繁创建和销毁对象会产生垃圾回收压力,垃圾回收一频繁,动画就会出现周期性 “跷跷板式”掉帧。

我见过有人为了“看起来更随机”,每个雨滴掉落之后都用new RainDrop()生成一个新对象并立即替换,这带来了明显的 GC 停顿。用对象池之后,一次探测到的 GC pause 从平均 7ms 降到了 2ms 以下,体感差别很大。

4.3 降级策略:低端机不要硬扛

不是所有手机都能承受 300 根雨丝逐帧刷新。合理的降级策略是渐进式的:检测到帧率连续低于某个阈值,就动态削减粒子数量。实现方式很直接,用requestAnimationFrame的回调时间戳计算 FPS,如果连续 30 帧低于 45,就把MAX_DROPS砍掉一半。

如果设备硬件本身较低端,还可以直接减少绘制透明层的叠加次数,或者把雨丝改成纯色短直线,不再使用半透明rgba。半透明混合的开销一般比纯色高,这在低端机上是有效果的。

另有更适合移动端的调整技巧:canvas 尺寸不要铺满全屏的物理分辨率,可以用逻辑分辨率加上ctx.scale(dpr, dpr)。在性能不足的机型上,还可以把dpr强制限制到 1.5 或 2,牺牲一点清晰度换流畅度。我实测过,dpr从 3 降到 2,帧率提升将近 20%,画面锐度损失肉眼几乎不可见。

4.4 移动端和低配电脑的实测表现

我找了几台不同档位的设备做过简单测试,数据大致是:

设备档位粒子数帧率表现调整建议
旗舰手机40058-60fps可维持
中端手机30045-55fps降到 220
入门口袋机20030-40fps降到 120,禁阴影
低配笔记本30050-55fps可维持,减少透明度层

如果你的页面本身已经有很多动画、图片懒加载、滚动监听,雨幕的预算还要再压缩。我一般建议雨幕占据整机渲染预算的 5% 到 10%,这才算一个合格的装饰层。超过这个比例,在复杂页面上很容易拖垮整体交互体验。

5. 常见问题与排查技巧实录

写特效的日常就是改 bug,雨幕也不例外。我把自己踩过的一些典型坑整理成一张速查表,外加几个比较隐蔽的排查技巧,遇到类似问题可以直接对照。

5.1 常见问题速查表

现场可能原因解决方案
雨丝全是小圆点只画了头没画尾,或者线段长度太短检查lineTo终点与起点 Y 坐标差
雨不动帧循环里忘记调用requestAnimationFrame确认递归调用存在
雨丝模糊canvas.width未按 DPR 设置按dpr设置物理像素并setTransform
大面积透明闪烁没调用clearRect,旧帧残留每帧开头先清空画布
滚动页面雨偏了canvas 定位错误检查position: fixed或滚动容器
低端机狂掉帧粒子数过多、半透明混合多按降级策略削减粒子数和透明度
明明 60fps 却出现周期性停顿频繁new对象导致 GC改成对象池复用粒子

5.2 几个容易忽视的深层问题

网格线问题对设计侧很致命。某些情况下,雨丝长度如果固定成 10 像素,那么所有雨丝间距离总保持 10 的倍数关系,视觉上会浮现横向的间隔线,看着像雨幕被整齐切割了。解决方案很简单:让雨丝长度在一个连续范围内随机分布,比如 8 到 20,而不是固定整数档位。

另一个问题来自 Canvas 的lineCap。默认lineCap是butt,线段端点呈直角,雨丝头部会显得僵硬,尤其在贴近屏幕顶部的瞬间。改成round之后,雨丝两端带半圆帽,视觉连续性和手绘质感会好很多。代价仅是轻微的性能损耗,可以忽略。

半透明雨的叠加问题更隐蔽。大量半透明雨丝叠加后,画面有时会越来越灰白,像起了雾。这是因为半透明线段互相叠加,颜色向白色靠拢。解决办法是控制总体的透明度上限,或者在绘制时统一降低雨丝颜色的蓝色通道,让整体色调偏冷、偏暗,而不是偏白。

5.3 让雨更有层次感的三个技巧

层次感最能拉开普通实现和高级实现的差距。我的做法有三个。

第一个是视差。把雨幕分成两个 canvas 层叠加,一个渲染疏雨、一个渲染暴雨,两层之间加不同的transform: translateY速度,滚动时高层雨更快地跟随鼠标,低层雨缓慢移动。这种多层视差给人的立体感非常明显。

第二个是时间调制。雨不是一直均匀下的,偶尔会出现一阵急促的雨,然后变缓。用正弦函数调制粒子刷新速率,就能实现“一阵一阵”的自然节奏。比如每帧给雨滴总数乘一个0.6 + 0.4 * Math.sin(t / 800)的系数,这比恒定数量的雨幕生动得多。

第三个是模拟近光和远光氛围。雨幕不要整屏统一亮度,中间区域可以稍亮一些,边缘稍暗。这种方式人眼天然会聚焦中部信息区,又不影响页面的可读性。实现起来也不难,在 canvas 上叠一层径向渐变遮罩,中心透明、边缘半透明即可。

最后聊两句实在的

效果写到这一步,回头看搜索引擎那些“玄学”结果,其实不冤。真正玄的从来不是技术,而是需求被简化到了不可执行的程度。哪怕只是一个下雨效果,背后也藏着粒子系统、动画循环、性能预算和大量调参经验。

按照我个人的习惯,只要项目没有硬性要求,我一直坚持“先 Canvas 2D,不上 WebGL,不上第三方库”。第三方依赖虽然能省事,但在动画领域,它们往往带来更大的体积和不可控的兼容成本。自己写 200 行代码,反而能在调参时获得最大的自由。

最后再分享一个平时不怎么有人提的技巧:调参时别直接看屏幕,先录一段慢动作视频。慢动作下你能清楚看到雨丝是平滑移动还是有抖动、循环边界是否穿帮、雨滴末梢是否生硬。修完慢动作里看到的问题,正常速度下的效果基本就能过关了。希望这套从拆需求到实现、优化的完整流程,能让你在做“页面下雨”这个需求时少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询