使用一组 CSS 关键帧动画到不同的最终状态

Avatar of Ana Tudor
Ana Tudor

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

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

粒子爆炸动画的 GIF 图。

本文旨在展示一个特别酷的技巧:**只有一组关键帧**用于移动屏幕上的所有粒子!还有一组关键帧负责淡出它们。但是,即使它们最终出现在屏幕上完全不同的位置,也只有一组关键帧负责控制这一点。

让我们看看它是如何工作的!

结构

这里没有太多内容。我们只是将 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 点平移它们,其中 x1vw100vw 之间,y1vh100vh 之间。random(100) 为我们提供了介于 1100(包括两者)之间的随机整数。

同时,我们还为这些粒子提供了不同的随机背景,以便我们可以看到它们。我们选择 hsl() 格式,因为它在这里最方便。random(360) 涵盖了整个 色相环,为我们提供了介于 1360 之间的随机值。

色相范围从 0 到 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

粒子射向平面上的不同位置,就像我们想要的那样。但它们同时开始动画,这不是我们想要的。因此,第一个解决方法是为每个粒子在循环中提供不同的随机动画时长,介于 1s3s 之间

.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