纯CSS打造现代飘雪动画效果

发表时间: 2023-11-25 12:00

今天北京也飘雪 ❄️ 了! 在日常 Web 开发中实现飘雪效果时,我们首先想到的是通过 JavaScript 来实现。现代 CSS 这么强大,能否直接使用 CSS 来实现呢。经过调研后,鉴于现代 CSS 的强大,确实可以使用纯 CSS 来实现飘雪动画效果。

1.核心知识

CSS 关键帧和动画是实现飘雪动画的关键,使用动画延迟、位置变换可以让视觉感知更为明显。同时让图标独立于布局,可以实现雪花像真正的雪花一样飘动。

主要的雪花实现方案有:

1)HTML 元素:

使用多个 HTML 元素来绘制雪花,这个实现起来比较简单,使用雪花 ICON 或者雪花字符(44 或 ❄)来充当雪花,这种实现起来比较简单,看着会比较真实。

2)背景图片:

使用背景图片,即 CSS 的 radial-gradient() 函数来绘制雪花,这个可以实现对每个雪花的样式灵活可控

CSS radial-gradient() 函数创建一个 <image>,用来展示由 原点(渐变中心) 辐射开的颜色渐变。这个方法得到的是一个 CSS 数据类型的对象,这是一种特殊的 <image>

基本语法:

radial-gradient( [ x [, y] ] [ circle | ellipse ] [ extent-keyword ] at [ position ] , [color-stop [, length | persentage]]+ )

2.实现方案

本文采用 CSS radial-gradient() 来实现雪花,通过 html:5div.snow 来快速创建页面框架:

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>Document</title>  </head>  <body>    <div class="snow"></div>  </body></html>

2.1.实现雪花元素

为了效果更真实,同时给 .snow 元素增加了两个伪元素 ::before::after 来增强效果。

.snow,.snow::before,.snow::after {  background-image:   radial-gradient( 4px 4px at 259px 455px, white 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 347px 72px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 345px 235px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(3px 3px at 152px 121px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient(5px 5px at 408px 540px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 131px 307px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 100px 410px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(4px 4px at 88px 302px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient(4px 4px at 582px 533px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 5px 5px at 488px 476px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 20px 470px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(6px 6px at 303px 513px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 50px 491px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 414px 30px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 287px 589px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 7px 238px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(5px 5px at 99px 579px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient(6px 6px at 118px 66px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient(5px 5px at 51px 396px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 101px 21px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 228px 115px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(4px 4px at 74px 168px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 3px 3px at 158px 9px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 338px 147px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 567px 57px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 210px 99px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 338px 556px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 536px 350px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 281px 36px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 139px 151px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 494px 202px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 594px 262px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 338px 239px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 389px 480px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 85px 391px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 271px 181px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 425px 237px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 239px 399px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 387px 592px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 307px 461px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 458px 142px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(6px 6px at 593px 136px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 458px 363px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 74px 549px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 543px 462px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 496px 547px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 239px 523px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(4px 4px at 216px 249px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 120px 195px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 130px 551px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(6px 6px at 328px 533px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 531px 461px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 58px 311px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(3px 3px at 139px 296px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 3px 3px at 57px 364px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 576px 512px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 248px 262px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 576px 244px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 452px 483px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 6px 6px at 154px 175px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(3px 3px at 593px 286px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 6px 6px at 255px 543px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 50px 443px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 23px 71px, rgba(255, 255, 255, 0.9) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 3px 3px at 182px 435px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 155px 384px, rgba(255, 255, 255, 0.8) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 5px 5px at 160px 594px, rgba(255, 255, 255, 0.7) 50%, rgba(0, 0, 0, 0) ),   radial-gradient(5px 5px at 213px 144px, white 50%, rgba(0, 0, 0, 0)),   radial-gradient( 3px 3px at 533px 373px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) ),   radial-gradient( 4px 4px at 519px 365px, rgba(255, 255, 255, 0.6) 50%, rgba(0, 0, 0, 0) );}

2.2.实现动画

通过给伪元素 ::before::after 增加动画延迟、透明度、过滤效果,增加动画效果和层次。

.snow,.snow::before,.snow::after {  animation: snow 3s linear infinite;}.snow::before {  opacity: 0.4;  animation-duration: 6s;  animation-direction: reverse;  filter: blur(3px);}.snow::after {  opacity: 0.65;  animation-duration: 9s;  animation-direction: reverse;  filter: blur(1.5px);}

3.最终效果