我最近现场编码了 一个纯 CSS 随机彩虹粒子爆炸效果。屏幕中间有一个源点,彩虹粒子在不同的时间以不同的速度射出,然后逐渐消失。这看起来像是需要大量工作和代码的事情,但实际上我很快就完成了,并且只使用了 30 行 SCSS 代码。

本文旨在展示一个特别酷的技巧:**只有一组关键帧**用于移动屏幕上的所有粒子!还有一组关键帧负责淡出它们。但是,即使它们最终出现在屏幕上完全不同的位置,也只有一组关键帧负责控制这一点。
让我们看看它是如何工作的!
结构
这里没有太多内容。我们只是将 400
个粒子放入 body
元素中。我使用了 Haml,因为我觉得它提供了最简单的循环方式,甚至不涉及我不需要的循环变量。请注意,这完全取决于个人喜好。我倾向于使用能以最少代码量提供我想要的结果的预处理器,因为我需要编写的代码越少越好。在这种特定情况下,碰巧是 Haml。但是,归根结底,任何允许您在循环中生成所有 .particle
元素的预处理器都可以正常工作。
- 400.times do
.particle
基本样式
我们首先要调整粒子的尺寸并进行绝对定位。我选择将它们设置为 4x4
的正方形,因为我想实现像素化的外观。
$d: 4px;
.particle {
position: absolute;
width: $d; height: $d;
}
屏幕上的随机定位
我们遍历这 400
个粒子(请注意,SCSS 中 @for
循环中的数字需要与我们在 Haml 循环中使用的数字相同),并在屏幕上的随机 x,y
点平移它们,其中 x
在 1vw
和 100vw
之间,y
在 1vh
和 100vh
之间。random(100)
为我们提供了介于 1
和 100
(包括两者)之间的随机整数。
同时,我们还为这些粒子提供了不同的随机背景,以便我们可以看到它们。我们选择 hsl()
格式,因为它在这里最方便。random(360)
涵盖了整个 色相环,为我们提供了介于 1
和 360
之间的随机值。
然后我们将饱和度最大化 (100%
) 并设置亮度高于 50%
(在本例中为 65%
)。
.particle {
/* same styles as before */
@for $i from 0 to 400 {
&:nth-child(#{$i + 1}) {
transform: translate(random(100)*1vw, random(100)*1vh);
background: hsl(random(360), 100%, 65%);
}
}
}
现在我们可以看到我们的粒子分布在整个屏幕上
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
它们会触发滚动条,因此我们在根元素上设置 overflow: hidden
。我们还为它提供了一个深色的 background
,以便我们能够更好地看到我们明亮的彩虹粒子
html {
overflow: hidden;
background: #222;
}
这样,我们就得到了一个漂亮的夜空
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
动画
下一步是为这些粒子设置动画,使它们从屏幕中间射出。这意味着我们的动画从 50vw,50vh
点开始。
@keyframes shoot {
0% { transform: translate(50vw, 50vh); }
}
我们没有指定最终 (100%
) 关键帧。如果未指定,则会根据我们已为要设置动画的元素设置的样式自动生成 - 在我们的案例中,这些是随机平移,每个粒子都不同。
我们希望粒子的运动一开始很快,然后逐渐慢下来,这意味着我们需要使用一种“缓出”类型的定时函数。我们可以直接使用普通的 ease-out
,或者可以使用更高级的函数(我使用 easings.net 来查找)。然后,我们为 animation
设置一个虚拟时长 3s
并使其无限重复。
.particle {
/* same styles as before */
animation: shoot 3s ease-out infinite;
}
我们得到了以下结果
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
粒子射向平面上的不同位置,就像我们想要的那样。但它们同时开始动画,这不是我们想要的。因此,第一个解决方法是为每个粒子在循环中提供不同的随机动画时长,介于 1s
和 3s
之间
.particle {
/* same styles as before */
@for $i from 0 to 400 {
$t: (1 + .01*random(200))*1s;
&:nth-child(#{$i + 1}) {
/* same styles as before */
animation-duration: $t;
}
}
}
这样好多了
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
然后,我们为每个粒子提供一个随机的负延迟,介于其 animation-duration
($t
) 的 0%
和 100%
之间(取绝对值)
.particle {
/* same styles as before */
@for $i from 0 to 400 {
$t: (1 + .01*random(200))*1s;
&:nth-child(#{$i + 1}) {
/* same styles as before */
animation-delay: -.01*random(100)*$t;
}
}
}
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
最后,我们不希望粒子只是消失,因此我们添加第二个动画(具有相同的时长和相同的延迟)使其淡出
.particle {
/* same styles as before */
animation: shoot 0s ease-out infinite;
animation-name: shoot, fade;
}
@keyframes fade { to { opacity: 0; } }
现在我们得到了最终结果!
查看 thebabydino (@thebabydino) 在 CodePen 上的 Pen。
太棒了!很棒的过程,写得也很漂亮。干得好。
真是个好主意!
这是一个星场:http://codepen.io/anon/pen/wgJQXx
太棒了。
哇,太棒了!
很酷的方法!
如果你在 .particle 上设置 opacity: 0 并在 shoot 上设置 opacity: 1,你甚至可以将淡出保留在一组关键帧中
没错。我将它们分开是为了能够使用不同的定时函数——一个用于运动,另一个用于淡出。但我最终放弃了这个想法,并且仍然坚持使用两组关键帧。
你好。这很棒 :D。
顺便说一句,可能没什么,但我认为这是一个错误
如果我在 sass 文档 中正确理解了
random
,random(100)
为我们提供了介于 1 和 100(包括两者)之间的随机整数。而不是零。即使我们将“介于”理解为“不包括区间限制”,这也是错误的,因为它不会包含零,但你也会省略 100。当然,random(360)
也适用。我知道这是一个很小的细节,但以防万一你想更正/澄清它,如果我没有错的话:)。无论如何,它不会影响获得的结果。
哎呀!现在已修复,谢谢!我应该实际检查一下
random()
的文档,但从未这样做,只是假设结果在[0, $limit)
区间内。