可重复交错动画三种方式:Sass、GSAP 和 Web 动画 API

Avatar of Opher Vishnia
Opher Vishnia

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

交错动画,也被称为 “跟进” 或 “重叠动作” 是由奥利·约翰斯顿和弗兰克·托马斯在他们 1981 年的著作《生命之幻觉》中定义的迪士尼动画十二原则之一。 其核心概念是通过延迟的顺序来动画化对象,以产生流畅的运动。

然而,这项技术并不仅仅适用于可爱的卡通人物动画。 数字界面的运动设计方面对 UX、用户感知和 “感受” 有 重大影响 谷歌甚至在其 运动编排 页面中提到了交错动画,作为 Material Design 指南的一部分

虽然运动设计这个主题非常广泛,但我经常发现自己即使在最小的项目中也会应用一些片段。 在 Eko 上的 可交互可口可乐广告 的设计过程中,我被要求创建一些动画,在交互式视频加载时显示,因此这个模型诞生了

乍一看,这个动画似乎很简单就能用 CSS 实现,但事实并非如此! 虽然使用 GSAP 和全新的 Web 动画 API 可能更简单,但使用 CSS 则需要一些技巧,我将在本文中解释。 那为什么还要使用 CSS 呢? 在这种情况下——由于动画旨在在用户等待资产加载时运行,因此加载动画库仅仅为了显示加载动画是没有意义的。

首先,关于动画的结构。

有四个圆圈,绝对定位在一个容器内,该容器具有 overflow: hidden 属性,用于框定和裁剪最外侧两个圆圈的边缘。 为什么是四个而不是三个? 因为第一个圆圈在屏幕外,等待从左侧进入舞台,最后一个圆圈从右侧退出舞台。 其他两个始终在框架中。 这样,动画迭代的结束状态看起来与它的起始状态完全一样。 圆圈 1 取代圆圈 2,圆圈 2 取代圆圈 3,依此类推。

这是基本的 HTML

<div id="container">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

以及相应的 CSS

#container {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 160px;
  height: 40px;
  display: block;
  overflow: hidden;
}
span {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #4df5c4;
  display: inline-block;
  position: absolute; 
  transform: translateX(0px);
}

让我们用一个简单的动画来尝试一下,每个圆圈的 X 轴从 0 到 60 像素进行平移

查看 CodePen 上的示例 点加载器 - 无交错,由 Opher Vishnia (@OpherV) 创建。

看起来有点奇怪和机械,对吧? 那是因为我们缺少一个主要组件:交错动画。 也就是说,每个圆圈的动画需要在其前一个动画之后开始。 “没问题!”,你可能会想,“让我们使用 animation-delay 属性。 我们将第 4 个圆圈的值设置为 0s,第 3 个设置为 0.15s,依此类推”。 好吧,让我们试试

查看 CodePen 上的示例 点加载器 - 错误,由 Opher Vishnia (@OpherV) 创建。

嗯… 发生了什么? animation-delay 属性只影响动画开始前的初始延迟。 它不会在每次迭代之间添加额外的延迟,因此动画会像下面的图表一样失去同步

数学来拯救

为了克服这个问题,我将延迟烘焙到动画中。 CSS 关键帧动画以百分比表示,通过一些计算,您可以使用它们来定义动画应包含多少延迟。 例如,如果将 animation-duration 设置为 1s,并将起始关键帧设置为 0%,将相同的数值设置为 20%,结束关键帧设置为 80%,将相同的结束数值设置为 100%,动画将等待 0.2 秒,运行 0.6 秒,然后再次等待 0.2 秒。

在我的例子中,我希望每个圆圈在进行持续 0.5 秒的实际动画之前等待 0.15 秒的交错时间,整个过程持续 1 秒。 这意味着第 4 个圆圈动画等待 0 秒,然后动画 0.5 秒,并等待另外 0.5 秒。 第 2 个圆圈等待 0.15 秒,然后动画 0.5 秒,并等待 0.35 秒,依此类推。

为了实现这一点,您需要四个关键帧(或三个关键帧对):1 和 2 用于交错等待,2 和 3 用于实际动画时间,而 3 和 4 用于最终等待。 “技巧” 是理解如何将所需的时间转换为关键帧百分比,但这只是一个相对简单的计算。 例如,第 2 个圆圈需要等待 0.15 * 2 = 0.3 秒,然后动画 0.5 秒。 我知道动画的总时间是一秒钟,因此关键帧百分比的计算方式如下

0s = 0%
0.3s = 0.3 / 1s * 100 =  30%
0.8s = (0.3 + 0.5) / 1s * 100 = 80%
1s = 100%

最终结果看起来像这样

由于整个动画(包括交错时间和等待时间)烘焙到 CSS 关键帧中,恰好持续一秒钟,因此动画不会失去同步。

幸运的是,Sass 允许我们使用一个简单的 for 循环和一些内联数学来自动化这个过程,最终编译成一系列关键帧动画。 这样,您可以操作时间变量来实验和测试对您的动画最有效的方法

@mixin createCircleAnimation($i, $animTime, $totalTime, $delay) {      
  @include keyframes(circle#{$i}) {
    0% {              
      @include transform(translateX(0));            
    }
    #{($i * $delay)/$totalTime * 100}% {     
      @include transform(translateX(0));            
    }          
    #{($i * $delay + $animTime)/$totalTime * 100}% {     
      @include transform(translateX(60px));            
    }          
    100% {
      @include transform(translateX(60px));             
    }
  }      
}

$animTime: 0.5s;
$totalTime: 1s;
$staggerTime: 0.15s;

@for $i from 0 through 3 {
  @include createCircleAnimation($i, $animTime, $totalTime, $staggerTime); 
  span:nth-child(#{($i + 1)}) {
    animation: circle#{(3 - $i)} $totalTime infinite;
    left: #{$i * 60 - 60 }px;
  }
}

瞧——这是最终的结果

<

p data-height=”450 data-theme-id=” 1″=”” data-slug-hash=”bEydYo” data-default-tab=”result” data-user=”OpherV” data-embed-version=”2″ data-pen-title=”dot loading animation – SASS stagger” class=”codepen”>查看 CodePen 上的示例 点加载动画 - SASS 交错,由 Opher Vishnia (@OpherV) 创建。

这种方法有两个主要的注意事项

首先,您需要确保定义的交错时间/动画时间不要过长,以免与动画总时间重叠,否则数学(和动画)将失效。

其次,这种方法会生成大量的 CSS 代码,尤其是在使用 Sass 来生成所有浏览器兼容性前缀时。 在我的示例中,我只对四个项目进行了动画处理,但如果您的项目包含更多项目,生成的代码量可能不值得付出努力,您可能希望坚持使用基于 JS 的动画库,例如 GSAP。 尽管如此,完全用 CSS 来完成这件事还是非常酷的。

让生活更轻松

为了对比 Sass 解决方案的冗长,我想向您展示如何使用 GSAP 的 Timeline 和 staggerTo 函数轻松实现相同的效果

查看 CodePen 上的示例 点加载动画 - GSAP,由 Opher Vishnia (@OpherV) 创建。

这里有两个有趣的地方。 首先,staggerTo 的最后一个参数定义了动画元素之间的等待时间,它被设置为负值 (-0.15)。 这使得元素能够以相反的顺序交错(圆圈 4-3-2-1 而不是 1-2-3-4)。 很酷,对吧?

其次,请查看 tl.set({}, {}, “1”); 这段代码。 这段代码有什么奇怪的语法? 这是一个实现每个圆圈动画结束时的等待时间的巧妙技巧。 本质上,通过在时间 1 将一个空对象设置为一个空对象,Timeline 动画将从 1 秒标记后开始重复,而不是在圆圈动画结束之后。

展望未来

Web 动画 API 是新一代令人兴奋的技术,但它超出了本文的范围。 我还是忍不住为您提供一个示例实现,它使用了与 CSS 实现相同的数学方法

查看 CodePen 上的示例 点加载动画 - WAAPI,由 Opher Vishnia (@OpherV) 创建。

这有帮助吗? 您是否使用过这种技术创建了一些流畅的动画? 告诉我!