使用 HTML 和 CSS 创建降雪效果
最近德克萨斯州一直在下雪,天气也很寒冷(现在仍然如此)......这就是我用 HTML 和 CSS在不到 10 分钟的时间内制作出这个雪花飘落的快速动画的灵感(视频位于页面底部)。
注意:我们分别使用了Pug和Sass/SCSS来简化 HTML 和 CSS 的重复部分,但它们并非必需。您可以推断代码仅包含 HTML 和 CSS(为简单起见,我们将在文章中同时展示这两种代码)。
这就是我们的动画最终呈现的样子(通过 CodePen 演示):
设置背景
我们先来设置背景。此步骤是可选的,可以通过多种方式完成。出于演示目的,我们仅使用 CSS 制作一个深色背景:
html, body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
background: linear-gradient(#123, #111);
}
添加雪花
然后,我们将为<div>
屏幕上想要显示的每个雪花创建一个。我们可以这样做:
<div class="snowflake"></div>
...
...
...
<div class="snowflake"></div>
<div class="snowflake"></div> <!-- 50 times! -->
但为了简单起见,我们使用了 PugJS,它允许我们使用循环来执行这些重复性任务:
- for (i = 0; i < 50; i++)
div(class="snowflake")
雪花造型
现在<div>
页面上已经有了所有的雪花,我们需要给它们添加样式。它们将是小巧、圆润、白色的:
.snowflake {
--size: 1vw;
width: var(--size);
height: var(--size);
background: white;
border-radius: 50%;
position: absolute;
top: -5vh;
}
我们对宽度和高度使用了自定义属性(--size
),因为当我们以后想要不同大小的雪花时会很方便。
另外,我们将雪花放置在视图框架之外(从顶部)。我们将使它们落到视图框架之外(从底部)。
添加动画
为了实现这个秋天的动画效果,我们需要使用 CSS 动画@keyframes
。我们将从一些基本的东西开始,然后让它逐渐发展。
首先,我们将使用translate3d
来使雪花垂直移动。因为它是一个 3D 变换,所以它会触发硬件加速,看起来比我们用其他属性来制作动画效果要好看得多,例如top
:
@keyframes snowfall {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(0, 110vh, 0);
}
}
我们可以通过添加以下属性将此动画应用于雪花类:
animation: snowfall 5s linear infinite;
但这只会让雪花从上到下垂直移动,看起来不真实。而且,由于绝对定位,所有雪花都重叠了,看起来不太好看。我们需要解决这个问题。
我们可以创建 50 条不同的规则,每个雪花一条,为每个雪花分配不同的左侧位置、角度、速度……虽然这在纯 CSS 中是可能的,但却非常繁琐:
.snowflake:nth-child(1) {
--size: 0.6vw;
left: 55vw;
animation: snowfall 8s linear infinite;
}
...
.snowflake:nth-child(49) {
--size: 1vw;
left: 78vw;
animation: snowfall 7s linear infinite;
}
.snowflake:nth-child(50) {
--size: 1.5vw;
left: 20vw;
animation: snowfall 10s linear infinite;
}
使用 SCSS 及其函数编写代码并使用生成的 CSS 代码会更加容易。因此,我们不必编写数百行代码,而是可以使用循环来大大简化开发过程:
@for $i from 1 through 50 {
.snowflake:nth-child(#{$i}) {
--size: #{random(5) * 0.2}vw; /* randomize size! */
left: #{random(100)}vw;
animation: snowfall #{5 + random(10)}s linear infinite;
}
}
轰!这 7 行代码稍后会被编译成 250 行!而且我们不必担心随机数的生成,因为 SCSS 提供了这方面的random()
功能。
最后的润色
雪花的大小和移动速度各不相同,但它们仍然只是垂直移动,这不太现实。我们可以将 CSS 变量与 SCSS 函数结合起来,添加一些随机的横向移动效果:
/* uses CSS variables to determine initial and final position */
@keyframes snowfall {
0% {
transform: translate3d(var(--left-ini), 0, 0);
}
100% {
transform: translate3d(var(--left-end), 110vh, 0);
}
}
@for $i from 1 through 50 {
.snowflake:nth-child(#{$i}) {
--size: #{random(5) * 0.2}vw;
--left-ini: #{random(20) - 10}vw; /* random initial translation */
--left-end: #{random(20) - 10}vw; /* random final translation */
left: #{random(100)}vw;
animation: snowfall #{5 + random(10)}s linear infinite;
animation-delay: -#{random(10)}s;
}
}
最后,我们添加了一个负片animation-delay
,这样所有雪花的动画开始位置就不会相同。否则,它们会同时开始飘落,看起来会有点奇怪。
更新:将变量移动到 HTML
上面的代码没问题,但它生成了很多重复的 CSS 规则,而且每次只会改变一个小的值。可以将其简化为类中的一个(或两个)属性.snowflake
,并为每个元素使用 CSS 变量。
这个想法是将 CSS 变量的声明从 CSS 移到 HTML:
<!-- 1 -->
<div class="snowflake" style="--left: 69vw; --left-ini: -4vw; --left-end: 0vw; --speed: 8s; --size: 0.4vw; --delay: -10s;"></div>
...
...
...
<!-- 50 -->
<div class="snowflake" style="--left: 83vw; --left-ini: 5vw; --left-end: 1vw; --speed: 10s; --size: 0.2vw; --delay: -6s;"></div>
在 PugJS 中,我们需要定义一个random
函数,然后使用它来设置 CSS 变量的值:
- function random(num) { return Math.floor(Math.random() * num) }
- for (i = 0; i < 50; i++)
div(class="snowflake", style=`--left: ${random(100)}vw; --left-ini: ${random(20) - 10}vw; --left-end: ${random(20) - 10}vw; --speed: ${5 + random(15)}s; --size: ${random(5) * 0.2}vw; --delay: -${random(15)}s;`)
现在我们在 HTML 中有了所有的值,我们可以删除所有的 CSS 选择器,而只在.snowflake
定义中直接保留动画属性:
.snowflake {
width: var(--size);
height: var(--size);
background: white;
border-radius: 50%;
position: absolute;
top: -5vh;
left: var(--left);
animation: snowfall var(--speed) linear infinite;
animation-delay: var(--delay);
}
这个解决方案显著简化了 CSS 代码(从 500 行减少到 34 行!),同时稍微增加了 HTML 的复杂度。您可以在这里查看它的运行情况。(感谢@afif的启发)。
结论
这是一个简单的动画,虽然很吸引人……但如果添加太多雪花,也会占用大量 CPU。请谨慎使用。
您可以在 Youtube 上观看动画开发的视频: