【译】如何只用CSS制作一个漂亮的加载动画
原文: How to create a beautiful animated loader with nothing but CSS
译者:百度外卖FE - 艾文
这是一篇用来展现伪元素和keyframe动画有多么吊炸天的创造力的文章,在这个例子中我们只用CSS来制作一个加载动画,不含任何JS代码或者图片。
为什么要做这个东西?
我的网站首页需要加载一段视频和一些图片,我不希望当用户进到页面以后很快的滚动到下方的时候看到这些东西还没加载出来,这样的体验很糟糕,我希望在这些东西加载出来之前他们能看到一段加载动画,直到内容加载完成后才能操作。
这就是为什么我决定做一个能够 尽可能迅速地显示出来 的加载动画,为了实现这个目的,我决定不用任何额外的图片或者JS代码去实现它,只用HTML和嵌入的CSS。
首先要说的是,这类东西的代码会根据不同的动画或者样式设计有不同的实现方法,我会用我的例子来展示一些思路或者通用的部分。
另外这篇文章假定读者已经熟悉了伪元素、CSS动画属性、keyframe动画等概念,关于这些内容不再赘述。如果你想阅读更多关于伪元素的内容, 戳这里 有你想知道的一切。如果你想了解更多关于CSS动画属性和keyframe动画的知识, 戳这里 。
好,正文开始
戳这里 去我的网站可以看到加载动画,如你所见,这个动画大体分为四个步骤:
1、边框一个接一个出现
2、红色,橙色,白色的矩形滑入
3、矩形们滑出
4、边框消失
实际上,我们只需要做第一步和第二步的动画,然后设置一个动画属性animation-direction: alternate,这会让CSS动画一遍正向一遍逆向的交替播放,这样第三步和第四步的动画也就有了。当然我们还要加上animation-iteration-count: infinite这个属性,来保证动画会永远重复下去。
大体的思路就是这样,下面我们从一个基本的HTML结构开始:
<!doctype html>
<!-- <link rel="preload"> for CSS, JS, and font files -->
<style type="text/css">
* All the CSS for the loader
* Minified and vendor prefixed
</style>
</head>
<div class="loader">
<!-- HTML for the loader -->
</div>
<header />
<main />
<footer />
<!-- Tags for CSS and JS files -->
</body>
</html>
接下来我不会直接就奔向最终目标,我们的第一步是制作一个不包含有动画的加载logo,也就是这个,首先是它的HTML,代码如下:
<div class="logo">
<div class="white"></div>
<div class="orange"></div>
<div class="red"></div>
</div>
div.logo是最外层的正方形,内部每个矩形用div.颜色的形式表示。每个div都是absolute布局,根据他们所在的位置设置top\bottom\left\right属性,并设置他们的宽度,代码如下:
div.logo {
width: 100px;
height: 100px;
border: 4px solid black;
box-sizing: border-box;
position: relative;
background-color: white;
div.logo > div {
position: absolute;
div.logo div.white {
border-left: 4px solid black;
top: 0;
bottom: 0;
right: 0;
width: 23%;
div.logo div.orange {
border-top: 4px solid black;
left: 0;
bottom: 0;
right: 0;
height: 50%;
background-color: #F3B93F;
div.logo div.red {
border-right: 4px solid black;
top: 0;
bottom: 0;
left: 0;
width: 27%;
background-color: #EA5664;
}
好的,接下来开始会有一些有趣的部分,因为CSS不允许我们直接给div.logo的边框加动画。所以我们不能直接用div.logo的边框,而要想点别的方法加上这东西,只有这样才能达到我们的目的。
也许我们可以把边框变成多个部分后,再分别按顺序显示?我们可以使用两个透明的伪元素覆盖在正方形的上面来实现这个效果,每个伪元素的边框覆盖正方形四条边框中的两条,然后我们做一个动画将伪元素的宽和高从0%改到100%,这样分别让每条边框动态的显示出来。
好的,让我们先创建一个静态、没有动画的版本。
我们用div.logo::before用absolute定位在左上角来代表div.logo的上边框和右边框,而div.logo::after定位在右下角来显示左边框和下边框。
代码如下:
div.logo::before, div.logo::after {
z-index: 1;
box-sizing: border-box;
content: '';
position: absolute;
border: 4px solid transparent;
width: 100%;
height: 100%;
div.logo::before {
top: 0;
left: 0;
border-top-color: black;
border-right-color: black;
div.logo::after {
bottom: 0;
right: 0;
border-bottom-color: black;
border-left-color: black;
}
下一步我们给div.logo::before加上keyframe动画。
这个keyframe的第一个状态的width和height都是0,之后用动画先将width加到100%,然后再将height加到100%。这里我让边框的颜色在正确的时机从透明切换到黑色,这会让上边框和右边框的动画完全按着我们的希望展示出来。
之后我们给div.logo::after加上类似的动画,当然不要忘了加上animation-direction: alternate用来做反向动画,这样我们就有了完整的边框动画。
动画代码如下:
div.logo::before, div.logo::after {
z-index: 1;
box-sizing: border-box;
content: '';
position: absolute;
border: 4px solid transparent;
width: 0;
height: 0;
animation-timing-function: linear;
div.logo::before {
top: 0;
left: 0;
animation: border-before 1.5s infinite;
animation-direction: alternate;
div.logo::after {
bottom: 0;
right: 0;
animation: border-after 1.5s infinite;
animation-direction: alternate;
@keyframes border-before {
0% {
width: 0;
height: 0;
border-top-color: black;
border-right-color: transparent;
24.99% {
border-right-color: transparent;
25% {
height: 0;
width: 100%;
border-top-color: black;
border-right-color: black;
50%,
100% {
width: 100%;
height: 100%;
border-top-color: black;
border-right-color: black;
@keyframes border-after {
49.99% {
width: 0;
height: 0;
border-left-color: transparent;
border-bottom-color: transparent;
50% {
width: 0;
height: 0;
border-left-color: transparent;
border-bottom-color: black;
74.99% {
border-left-color: transparent;
border-bottom-color: black;
75% {
height: 0;
width: 100%;
border-left-color: black;
border-bottom-color: black;
100% {
width: 100%;
height: 100%;
border-left-color: black;
border-bottom-color: black;
}
最后我们给内部的矩形加上动画。
这块主要的挑战在于我们不能直接串起来帧动画,我们需要在安排每段动画的时候考虑到其他的动画步骤,这样整个动画才能顺序显示,并且可以逆序显示。
对于边框的动画来讲,我们只是简单给每个边框25%的时间。我们需要在这期间加上矩形,在一系列的试错之后,我决定让动画总的时长是1.5秒,然后分别在下面这些时间段做这些动作:
1、0% to 25%:上边框和右边框出现
2、25% to 50%:下边框和左边框出现
3、50% to 65%:红色矩形出现
4、65% to 80%:橙色矩形出现
5、75% to 90%:白色矩形出现
根据上面这个时间线,我们做出来最终的成品,所有的CSS代码如下:
html, body {
height: 100%;
width: 100%;
overflow: hidden;
margin: 0;
div.loader {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
div.logo {
width: 100px;
height: 100px;
box-sizing: border-box;
position: relative;
background-color: white;
div.logo::before, div.logo::after {
z-index: 1;
box-sizing: border-box;
content: '';
position: absolute;
border: 4px solid transparent;
width: 0;
height: 0;
animation-direction: alternate;
animation-timing-function: linear;
div.logo::before {
top: 0;
left: 0;
animation: border-before 1.5s infinite;
animation-direction: alternate;
div.logo::after {
bottom: 0;
right: 0;
animation: border-after 1.5s infinite;
animation-direction: alternate;
div.logo > div {
position: absolute;
opacity: 0;
div.logo div.white {
border-left: 4px solid black;
top: 0;
bottom: 0;
right: 0;
width: 0;
animation: white 1.5s infinite;
animation-direction: alternate;
div.logo div.orange {
border-top: 4px solid black;
left: 0;
bottom: 0;
right: 0;
height: 0;
background-color: #F3B93F;
animation: orange 1.5s infinite;
animation-direction: alternate;
div.logo div.red {
border-right: 4px solid black;
top: 0;
bottom: 0;
left: 0;
width: 0;
background-color: #EA5664;
animation: red 1.5s infinite;
animation-direction: alternate;
@keyframes border-before {
0% {
width: 0;
height: 0;
border-top-color: black;
border-right-color: transparent;
12.49% {
border-right-color: transparent;
12.5% {
height: 0;
width: 100%;
border-top-color: black;
border-right-color: black;
25%,
100% {
width: 100%;
height: 100%;
border-top-color: black;
border-right-color: black;
@keyframes border-after {
24.99% {
width: 0;
height: 0;
border-left-color: transparent;
border-bottom-color: transparent;
25% {
width: 0;
height: 0;
border-left-color: transparent;
border-bottom-color: black;
37.49% {
border-left-color: transparent;
border-bottom-color: black;
37.5% {
height: 0;
width: 100%;
border-left-color: black;
border-bottom-color: black;
50%,
100% {
width: 100%;
height: 100%;
border-left-color: black;
border-bottom-color: black;
@keyframes red {
50% {
width: 0;
opacity: 0;
50.01% {
opacity: 1;
65%,
100% {
opacity: 1;
width: 27%;
@keyframes orange {
65% {
height: 0;
opacity: 0;
65.01% {
opacity: 1;
80%,
100% {
opacity: 1;
height: 50%;
@keyframes white {
75% {
width: 0;
opacity: 0;
75.01% {
opacity: 1;