【译】50 行 CSS 代码撸一个阴阳八卦的 Loading 动效
背景
网上做 Loading 动效的教程和工具比比皆是,用八卦图的还是头一次听说 =,=
因为一直都比较喜欢这种小而精、并且能让人眼前一亮的玩法。万万没想到居然还是个外国程序媛(推特请戳)的作品。
果断翻译过来供大家参考。
正文
不久前我见到了这样的动画。这让我有了一个想法即:我要在不使用外部的库的情况下,用尽可能少的代码、使用多样化的方法(其中包括一些如今可以使用的新功能,如 CSS 变量)创建我自己的版本。
本文将引导你完成构建这些 Demo 的过程。
在介绍其他任何步骤之前,先给出我最终要实现的动画效果,如下:
期望的效果: 一个旋转的八卦图,伴随着两个 “叶片” 大小循环地增加和缩小
从何处开始?
无论我们选择使用哪种方式重新创作上述动画,我们总是会先从静态阴阳形状开始,如下所示:
静态的阴阳八卦图
该起始静态形状的结构如下图所示:
静态八卦图的结构
首先,我们有一个直径为 d 的大圆。在这个圆内,我们紧紧地嵌入两个较小的圆圈,每个圆圈的直径都是我们初始大圆直径的一半。 这意味着这两个较小圆的每一个的直径等于大圆的半径 r(或 0.5 * d)。在直径为 r 的这两个较小圆的内部,我们有一个更小的同心圆。
如果我们画出通过所有这些圆的所有中心点的大圆的直径 - 上图中的线段 AB,它与内圆之间的交点将其分成 6 个相等的较小的段。 这意味着其中一个最小圆的直径为 r/3(或 d/6),其半径为 r/6。
纯 HTML + CSS 版
在这种情况下,我们可以用一个元素和它的两个伪元素来实现。 以下动画说明了创建两个“叶片”的方式(因为整个面板将会旋转,所以切换对称轴无关紧要):
实际的元素是外层的大圆,它有一个从上到下的渐变,中间有一个尖锐的过渡。 伪元素是我们放置的较小圆。一个较小圆的直径是大圆的直径的一半。 两个较小的圆都与大圆垂直中心对齐。
开始编写代码!
首先,我们决定大圆圈的直径 $d
。 我们使用 viewport 单位,以便在调整大小时可以很好地扩展。 我们将这个直径值设置为其 width
和 height
,使用 border-radius
使元素圆形化,并给出一个从上到下的渐变背景,中间从到黑色到白色的过渡。
|
|
So far, so good:
现在我们来看看我们用伪元素创建的较小的圆。 我们给我们的元素显示:通过设置align-items:center
,使它的孩子(或我们的例子中的伪元素)中间垂直对齐。 我们使这些伪元素具有其父元素的一半高度(50%),并且确保它们水平地覆盖大圆圈的一半。 最后,我们将它们与border-radius
进行整合,给它们一个虚拟背景,并设置内容属性,以便我们可以看到它们:
|
|
接下来,我们需要给他们不同的背景:
|
|
有点像那么回事儿了对吧!
在我们得到静态形状之前,要做的就是给这两个伪元素添加边框。 黑色应该是一个白色的边框,而白色的黑色边框应该是黑色的。 这些边界应该是伪元素直径的三分之一,这是大圆圈直径的三分之一,即 $d/6
。
|
|
但是,结果看起来并不正确:
这是因为在垂直方向上,边框被加到了height
。 水平地,我们没有设置宽度,所以边框可以从 content
空间获得。 有两个修复方法:一个是在伪元素上设置border-size:border-box
;第二个是将伪元素的高度更改为$d/6
- 我们将使用后者:
我们现在有了基本的八卦形状,所以让我们继续做动画!
这个动画从第一个伪元素缩小的状态开始,让我们设定原始大小为一半(这意味着缩放因子$f
为 0.5),而第二个伪元素已经扩展到占用所有可用的空间即:大圆圈的直径(原始尺寸的两倍)减去第一个圆圈的直径(即其原始尺寸的$f
); 然后状态改变到第二个伪元件的缩小到$f
的状态,第一个伪元素已扩展到其原始大小的2 - $f
。
第一个伪元素圆相对于其最左点(因此我们需要设置0 50%的变换原点),而第二个相对于其最右点(100%50%)进行缩放。
|
|
我们现在有了形状变化的动画:
最后一步是使整个形状旋转起来:
|
|
我们得到了最后的结果!
然而,还有一件事情我们可以做,使编译的 CSS 更有效率:消除冗余与 CSS 变量!
白色可以以 hsl(0,0%,100%)
的形式写入 HSL 格式。 色调和饱和度无关紧要,任何 100% 亮度的值都是白色的,所以我们将它们设置为 0,使我们的代码执行效率更高。 类似地,黑色可以写成 hsl(0,0%,0%)
。 再次,色调和饱和度无关紧要,任何亮度为 0% 的值都是黑色的。 鉴于此,我们的代码变成:
|
|
从上述结果可以看出:
- 我们的转换起点的 x 分量是第一个伪元素的
calc(0 * 100%)
,第二个是calc(1 * 100%)
- 我们的边框颜色是第一个伪元素的
hsl(0,0%,calc((1 - 0)* 100%))
和第二个hsl(0,0%,calc((1 - 1)* 100%))
- 我们的背景是第一个伪元素的
hsl(0,0%,calc(0 * 100%))
和第二个伪元素的hsl(0,0%,calc(1 * 100%))
- 我们的动画延迟是第一个伪元素的
calc(0 *#{ - $ t})
,第二个的calc(1 *#{ - $ t})
这意味着我们可以使用一个用作开关的自定义属性 --i
,第一个伪元素为 0,第二个为伪元素为 1:
|
|
这样就消除了所有这些规则两次的编写:我们现在需要做的就是翻转开关!
可悲的是,现在只能在 WebKit 浏览器中使用,因为 Firefox 和 Edge 不支持使用calc()
作为动画延迟值,Firefox 不支持在 hsl()
当中中使用它。 =^=
Canvas + JavaScript
SVG + JavaScript
结语
代码中的奇淫技巧固然重要,但归根结底,好的 idea 才是技术进步的源泉。
如果觉得文章对你有帮助的话,去 Github Repo 给个 star 吧亲~