尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

纯CSS实现下雨动画:线性渐变与关键帧动画的创意应用

纯CSS实现下雨动画:线性渐变与关键帧动画的创意应用 1. 项目概述用CSS“画”一场雨前端开发做久了总想找点乐子。当静态页面和常规动效已经满足不了你的创作欲时用纯CSS模拟一些自然现象就成了一个绝佳的“玩票”选择。今天要聊的就是如何用纯粹的CSS代码在网页上“下”一场雨。这听起来有点天方夜谭毕竟CSS是样式表语言不是动画引擎。但恰恰是这种“用非专业工具实现专业效果”的思路最能锻炼我们对CSS属性的深度理解和创造性思维。这个“简单的下雨效果”项目核心目标不是追求物理引擎级别的逼真模拟而是用最精简的代码通过CSS的动画和伪元素等特性营造出一种视觉上可信的、连续的雨滴下落氛围。它非常适合用在个人博客的背景、作品集的装饰区块或者一些需要营造特定情绪的小型H5页面中。实现原理并不复杂但其中关于性能优化、动画节奏控制以及视觉细节处理的技巧却是每个前端开发者都应该掌握的实战经验。接下来我们就从零开始拆解这场“雨”是如何一滴滴落下的。2. 核心思路与方案设计2.1 效果拆解雨滴的视觉构成在动手写代码之前我们需要像画家观察景物一样先拆解“下雨”这个视觉现象。一场雨由无数雨滴组成而每一滴雨在屏幕这个2D平面上可以抽象为几个关键特征形态现实中下落的雨滴由于速度很快在视觉上通常是一条细长的、略带模糊的直线或短线顶端可能稍圆末端尖锐。在CSS中我们可以用一个非常窄的矩形div加上圆角来近似模拟。运动雨滴基本是垂直向下运动但可能伴有轻微的随机左右偏移模拟风的效果。运动速度有快有慢形成层次感。出现与消失雨滴从视窗上方之外出现下落穿过整个视窗最后在视窗下方之外消失。这是一个典型的“入场-过场-退场”动画。层次与密度远处的雨滴看起来细、密、速度慢且对比度低近处的雨滴看起来粗、疏、速度快且对比度鲜明。我们需要用多个图层来表现这种空间纵深。基于以上分析我们的技术方案就清晰了创建多个代表不同层次雨滴的容器每个容器内通过伪元素批量生成雨滴线条并为这些线条赋予随机的动画延迟、持续时间和位置从而组合成连绵不断的雨景。2.2 技术选型为什么是纯CSS你可能会问用JavaScriptJS来实现不是更灵活吗确实JS可以更精确地控制每一个雨滴的物理属性。但我们这个项目的标题和初衷就是“有趣的CSS”其价值在于零依赖与高性能纯CSS动画通常由浏览器合成器线程处理性能开销极低尤其是在移动设备上比用JS频繁操作DOM要流畅得多。挑战与乐趣在CSS的约束下解决问题能极大提升对animation、keyframes、transform、opacity等属性的掌握深度。优雅降级即使JS被禁用或加载失败动画效果依然存在虽然可能缺少交互。学习价值这是理解CSS动画性能瓶颈如重绘与重排、以及如何利用硬件加速的绝佳案例。因此我们将完全使用HTML和CSS来完成。HTML结构将极其简单主要工作都在CSS中。2.3 整体架构设计我们将设计三层雨滴来模拟景深效果远景层雨滴细、多、透明度低、下落速度慢。负责营造雨幕的背景氛围。中景层雨滴适中是视觉效果的主体。近景层雨滴粗、少、透明度高、下落速度快。能带来较强的动感和视觉冲击。每一层都是一个独立的div容器我们将其绝对定位铺满整个屏幕。雨滴本身并不直接写在HTML里而是通过容器的::before和::after伪元素配合CSS的线性渐变背景来“画”出多条雨滴然后让这个背景图运动起来。这是实现大批量雨滴且保持低DOM复杂度的关键技巧。注意使用伪元素和背景动画的方式而不是创建成百上千个独立的div是为了最大限度地减少浏览器需要处理的DOM节点数量这对于性能至关重要。每一个DOM元素都是性能开销而伪元素和背景图的开销要小得多。3. 核心细节解析与实操要点3.1 雨滴的绘制线性渐变的妙用如何用CSS“画”出一条雨滴最优雅的方式是使用linear-gradient线性渐变。一条雨滴本质上是一个从上到下从半透明到透明或从一种颜色到另一种颜色的细长条。.raindrop { /* 这是一个示例实际我们不会单独创建这么多元素 */ width: 1px; height: 60px; background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 100%); border-radius: 50% 50% 20% 20%; /* 让顶部和底部呈现不同的圆角模拟水滴形状 */ }但在我们的方案中我们不会为每一滴雨创建一个这样的元素。相反我们为整个雨层容器创建一个巨大的背景图这个背景图由许多这样的“雨滴条纹”平铺而成。这通过background-image配合多个linear-gradient来实现。.rain-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; /* 关键创建一个包含多条透明渐变的背景图像 */ background-image: linear-gradient(to bottom, transparent 90%, rgba(255,255,255,0.2) 100%), linear-gradient(to bottom, transparent 85%, rgba(255,255,255,0.15) 100%); /* 让背景图在X轴上重复形成多列雨滴 */ background-size: 4px 100%; /* 每个背景条宽4px高度100%容器 */ background-repeat: repeat-x; }这里background-size: 4px 100%定义了每个背景单元的宽度是4px即雨滴之间的水平间距高度撑满容器。background-repeat: repeat-x让这个单元在水平方向重复铺满瞬间就形成了整排的雨滴。我们叠加多个不同透明度和截止点的渐变可以让雨滴看起来更自然有粗细和透明度的变化。3.2 动画驱动keyframes 与 animation雨滴下落的核心是垂直移动。我们将通过keyframes定义一个从上方移动到下方的动画并将其应用到背景图的位置属性上。keyframes fall { from { background-position: 0 -100%; /* 背景图从顶部之外开始 */ } to { background-position: 0 100%; /* 背景图移动到容器底部之外 */ } } .rain-layer { animation: fall 1.5s linear infinite; }background-position的y轴从-100%到100%意味着背景图从完全位于容器上方运动到完全位于容器下方从而产生了雨滴连续不断穿过屏幕的效果。animation的infinite参数让这个动画无限循环。3.3 随机性与层次感的关键CSS变量与计算一场真实的雨雨滴不会如此整齐划一。我们需要引入随机性不同的下落速度、不同的粗细、不同的透明度。但CSS本身没有随机函数。我们如何模拟技巧一使用CSS变量和动画延迟。我们可以为每一层雨定义不同的动画时长和延迟并利用nth-child或为不同层直接设置不同类名来实现差异。.rain-layer--far { /* 远景慢、细、透明 */ --drop-width: 1px; --animation-duration: 2.5s; opacity: 0.3; } .rain-layer--mid { /* 中景中等 */ --drop-width: 1.5px; --animation-duration: 1.8s; opacity: 0.6; } .rain-layer--near { /* 近景快、粗、实 */ --drop-width: 2px; --animation-duration: 1s; opacity: 0.9; } .rain-layer { background-size: var(--drop-width) 100%; animation: fall var(--animation-duration) linear infinite; }技巧二多层叠加与错位。仅仅三层还不够自然。我们可以创建多个同属“远景”或“中景”的div但给它们设置不同的background-position初始值水平偏移和animation-delay这样即使动画参数相同雨滴的起始位置和时间也不同叠加起来就更像随机雨幕。div classrain-container div classrain-layer rain-layer--far style--start-pos: 0%;/div div classrain-layer rain-layer--far style--start-pos: 33%;/div div classrain-layer rain-layer--far style--start-pos: 66%;/div div classrain-layer rain-layer--mid/div div classrain-layer rain-layer--near/div /div.rain-layer--far { background-position: var(--start-pos, 0) -100%; }实操心得animation-delay使用负值是一个高级技巧。animation-delay: -0.5s;意味着动画会从它整个周期的0.5秒处开始播放。这样你可以让多个相同周期的动画实例处于不同的播放阶段从而立即形成一个“已经在下雨”的连续状态而不是等待所有雨滴从起点开始。这对于背景氛围动画非常有用。4. 完整实现步骤与代码详解4.1 HTML结构搭建HTML结构极其简洁就是一个全屏的容器里面嵌套多个雨层。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title纯CSS下雨效果/title link relstylesheet hrefstyle.css style /* 基础样式为演示提供深色背景 */ body { margin: 0; padding: 0; height: 100vh; background: linear-gradient(to bottom, #0a0a2a, #1a1a40); overflow: hidden; display: flex; justify-content: center; align-items: center; color: #ccc; font-family: sans-serif; } .demo-text { position: relative; z-index: 10; text-align: center; padding: 2em; background: rgba(0, 0, 0, 0.5); border-radius: 10px; } /style /head body div classdemo-text h1CSS模拟下雨效果/h1 p观察背景这是一场由纯CSS驱动的雨。/p /div !-- 下雨效果容器 -- div classrain-container !-- 远景层 (多复制几个增加密度和随机感) -- div classrain-layer layer-far/div div classrain-layer layer-far style--x-offset: 20px;/div div classrain-layer layer-far style--x-offset: 40px;/div !-- 中景层 -- div classrain-layer layer-mid/div !-- 近景层 -- div classrain-layer layer-near/div /div /body /html4.2 CSS核心样式实现以下是style.css的完整内容包含了所有核心样式和动画。/* style.css */ .rain-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; /* 确保雨滴不会干扰页面其他元素的交互 */ z-index: 1; } /* 雨滴层的通用样式 */ .rain-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; /* 使用多个线性渐变叠加创建雨滴条纹。 格式linear-gradient(方向, 颜色 停止点, 颜色 停止点...) 这里创建从透明到半透明白色再到透明的条纹模拟雨滴的拖尾和模糊感 */ background-image: linear-gradient(to bottom, transparent 0%, rgba(255, 255, 255, 0.1) 10%, rgba(255, 255, 255, 0.3) 30%, transparent 100% ); /* 关键背景图尺寸。宽度(--drop-width)控制雨滴间距高度100% */ background-size: var(--drop-width, 2px) 100%; background-repeat: repeat-x; /* 初始背景位置。--x-offset用于水平错位y轴从-100%开始屏幕上方 */ background-position: calc(var(--x-offset, 0px) - var(--drop-width, 2px)) -100%; /* 应用下落动画 */ animation: fall-down var(--duration, 1s) linear infinite; /* 使用负延迟让动画立即进入循环状态的不同阶段 */ animation-delay: var(--delay, 0s); opacity: var(--opacity, 0.7); } /* 下落动画定义 */ keyframes fall-down { from { /* 从屏幕上方开始 */ background-position-y: -100%; } to { /* 移动到屏幕下方 */ background-position-y: 100vh; /* 使用视窗高度单位更精确 */ } } /* 远景层样式细、慢、淡 */ .layer-far { --drop-width: 1px; /* 雨滴很细 */ --duration: 3s; /* 下落速度慢 */ --opacity: 0.2; /* 透明度低 */ --delay: -0.7s; /* 动画延迟 */ /* 可以增加背景图复杂度让远景雨滴更稀疏模糊 */ background-image: linear-gradient(to bottom, transparent 80%, rgba(255,255,255,0.05) 95%, transparent 100%); } /* 中景层样式适中 */ .layer-mid { --drop-width: 1.8px; --duration: 1.8s; --opacity: 0.5; --delay: -0.3s; } /* 近景层样式粗、快、实 */ .layer-near { --drop-width: 2.5px; --duration: 1s; --opacity: 0.8; --delay: -0.1s; /* 近景雨滴可以更“实”一些减少模糊 */ background-image: linear-gradient(to bottom, transparent 0%, rgba(255, 255, 255, 0.9) 15%, rgba(255, 255, 255, 0.2) 40%, transparent 100% ); } /* 为了增加随机感我们可以利用:nth-child选择器微调同一层内不同实例的参数 */ .layer-far:nth-child(2) { --delay: -1.5s; --x-offset: 15px; } .layer-far:nth-child(3) { --delay: -2.2s; --x-offset: 30px; opacity: 0.15; }4.3 代码逐行解析与参数调整指南pointer-events: none;这个属性非常重要。它让.rain-container及其子元素不会响应鼠标事件如点击、悬停这样雨滴效果就不会盖住页面正文内容的交互。background-image这是我们“画”雨滴的笔刷。linear-gradient的to bottom表示从上到下的渐变。transparent和rgba(255,255,255,alpha)的交替使用创造了一条不透明的“线”雨滴和周围透明的区域。调整颜色和透明度(alpha)值可以改变雨滴的亮度和虚实程度。调整百分比停止点如10%,30%可以改变雨滴的“长度”和头尾的渐变效果。background-size: var(--drop-width, 2px) 100%;这是控制雨滴密度和粗细的核心。--drop-width这个CSS自定义属性变量在后面的.layer-far等类中被定义。宽度值越小雨滴看起来越细同时水平重复的雨滴列数就越多因为背景图在水平方向重复铺满。高度100%确保渐变条纹贯穿整个容器。background-position初始的background-position-y: -100%让背景图也就是第一排雨滴从容器顶部之外开始。在动画中我们将其改变为100vh视窗高度使其移动到屏幕底部之外。background-position-x我们通过--x-offset变量进行微调让不同层的雨滴水平起始位置错开避免完全对齐显得生硬。animation属性fall-down是动画名称var(--duration)控制单次动画周期时长速度linear是匀速运动infinite无限循环。var(--delay)特别是负值延迟用于错开各层雨滴的动画相位。CSS变量Custom Properties我们大量使用了--drop-width、--duration、--opacity、--delay等变量。这使得在HTML中通过类名如layer-far批量控制一整套样式变得非常方便和清晰也易于维护和调整。参数调整实战如果你想下“暴雨”可以尝试减小--drop-width如0.8px增加雨滴密度。缩短--duration如0.7s让雨下得更急。提高--opacity如0.9让雨更明显。在.rain-container里复制更多.rain-layer元素。 反之调整这些参数可以得到“毛毛雨”或“细雨”的效果。5. 高级优化与效果增强5.1 性能优化触发硬件加速CSS动画的性能很大程度上取决于是否触发了GPU硬件加速。最常用的方法是使用transform和opacity属性进行动画因为这两个属性可以被浏览器优化避免重排Reflow和重绘Repaint。在我们的方案中动画作用在background-position上。在现代浏览器中对background-position进行动画通常也能得到较好的优化但为了极致性能我们可以考虑一个替代方案使用transform: translateY来移动整个雨层容器。/* 优化版动画定义 */ keyframes fall-down-optimized { from { transform: translateY(-100%); } to { transform: translateY(100vh); } } .rain-layer { /* 移除旧的background-position动画 */ /* animation: fall-down var(--duration, 1s) linear infinite; */ /* 应用新的transform动画 */ animation: fall-down-optimized var(--duration, 1s) linear infinite; /* 其他样式不变 */ background-position: var(--x-offset, 0px) 0; /* 背景位置y轴初始为0 */ }这个改动将动画对象从背景图位置变成了整个元素的位置变换。transform属性在大多数情况下能更稳定地触发GPU加速动画会更加平滑尤其是在低性能设备或复杂页面上。你需要将雨滴背景的初始位置也调整到与容器顶部对齐。5.2 增加风效让雨丝倾斜真实的雨很少是绝对垂直的。我们可以通过skew或rotate变换让整个雨层容器倾斜模拟风吹的效果。.rain-layer { /* 在原有样式基础上添加 */ transform: skewX(-15deg); /* 让雨滴向左倾斜15度 */ /* 如果使用了上面的transform动画需要合并变换注意顺序 */ /* animation: fall-down-optimized 1s linear infinite; */ /* 此时动画中的transform会覆盖这里的skew所以需要写在动画关键帧里 */ } /* 或者在动画关键帧中整合倾斜和下落 */ keyframes fall-down-with-wind { from { transform: translateY(-100%) skewX(-10deg); } to { transform: translateY(100vh) skewX(-10deg); } }5.3 交互可能性用CSS变量实现动态控制虽然我们是纯CSS项目但可以通过结合一点点HTML和CSS变量的能力实现简单的交互。例如用复选框checkbox切换雨的大小。input typecheckbox idheavy-rain-toggle label forheavy-rain-toggle暴雨模式/label#heavy-rain-toggle:checked ~ .rain-container .layer-mid, #heavy-rain-toggle:checked ~ .rain-container .layer-near { --duration: 0.6s; --opacity: 0.9; filter: blur(0.3px); /* 增加一点模糊模拟速度感 */ } #heavy-rain-toggle:checked ~ .rain-container .layer-far { --drop-width: 0.7px; --duration: 1.5s; }当复选框被勾选时通过CSS选择器找到对应的雨层并覆盖其CSS变量瞬间改变动画参数实现效果切换。这展示了CSS变量在动态主题和交互样式中的强大威力。6. 常见问题、排查与浏览器兼容性6.1 效果不流畅或卡顿问题动画看起来有卡顿、跳帧。排查与解决检查性能打开浏览器开发者工具F12的“性能(Performance)”或“渲染(Rendering)”面板勾选“Paint flashing”。如果页面元素频繁闪烁绿色表示重绘说明性能开销大。尝试使用5.1节提到的transform替代background-position动画。减少图层和复杂度检查是否创建了过多的.rain-layer元素。通常远景层2-3个中景和近景各1个就足够了。过多的绝对定位元素叠加会增加合成层管理开销。降低密度增大--drop-width的值减少水平方向重复的雨滴列数。确保使用硬件加速为动画元素添加will-change: transform;属性可以提示浏览器提前优化。但不要滥用仅用于确实需要动画的元素。6.2 雨滴看起来太“硬”或不自然问题雨滴线条边缘锐利像一根根针缺乏雨水的模糊和动感。排查与解决优化渐变调整background-image中linear-gradient的色标。让透明到半透明的过渡更平滑区域更广。例如将rgba(255,255,255,0.3) 30%改为rgba(255,255,255,0.2) 20%, rgba(255,255,255,0.4) 40%创造一个更宽的半透明区域。添加模糊滤镜为雨层添加微小的filter: blur(0.3px);。半像素左右的模糊可以极大地柔化边缘模拟运动模糊。注意滤镜有性能成本谨慎使用。叠加多层不同透明度的渐变就像我们在示例代码中做的那样在一个background-image属性里叠加多个渐变可以创造出更丰富的透明度和形状变化。6.3 浏览器兼容性注意事项我们的方案主要依赖CSS动画、渐变和CSS变量Custom Properties。特性兼容性降级方案CSS动画 (keyframes,animation)IE10 所有现代浏览器对于不支持的浏览器如IE9动画不会播放页面静态显示雨滴背景通常可接受。CSS线性渐变 (linear-gradient)IE10 所有现代浏览器在不支持的浏览器中背景可能显示为纯色或失效。可提供一个简单的备用背景色。CSS变量 (--var)IE不支持 Edge 15 Chrome 49 Firefox 31这是最大的兼容性痛点。如果需要在旧浏览器运行必须放弃CSS变量直接将值写死在各个类中或者使用Sass/Less等预处理器变量在编译时替换。vh单位IE9 (部分支持) 所有现代浏览器在keyframes中使用vh可能导致IE/旧Edge动画异常。回退方案是使用100%但这依赖于容器高度必须明确为100vh。兼容性处理建议对于个人项目或面向现代浏览器的场景可以放心使用。如果需要兼容IE建议移除CSS变量用固定值或预处理器。将动画中的100vh改为100%并确保.rain-container的父级通常是body/html高度设置为100vh。考虑使用JavaScript特性检测库如Modernizr来为不支持关键特性的浏览器提供简化样式或提示。6.4 在滚动页面上应用如果下雨效果不是用于全屏背景而是页面中的一个区块且页面可滚动直接使用上述代码可能会导致动画区域错乱。问题雨滴动画可能只相对于它自己的容器运动当页面滚动时效果很奇怪。解决关键在于background-position的动画。如果容器不是全屏确保动画的起始和结束位置是相对于容器本身的高度。通常将keyframes中的100vh改为100%即可这表示背景图移动到容器自身高度的100%处即底部。同时确保容器overflow: hidden。.rain-container { position: relative; /* 非全屏时通常用relative */ width: 300px; height: 200px; overflow: hidden; } keyframes fall-for-container { from { background-position-y: -100%; } to { background-position-y: 100%; /* 改为容器高度的100% */ } }这场用CSS编织的雨从构思到实现涉及了CSS背景、动画、变量、伪元素乃至性能优化的多个知识点。它不像一个功能性的组件那样有直接的业务价值但它像一段前端工程师的“抒情诗”展示了在有限的语法里创造无限可能的乐趣。调整每一个渐变色标微调动画的延迟和时长看着屏幕上从生硬的线条变为生动的雨幕这个过程本身就是一种享受。
返回列表