supce's blog

CSS Secret 读书笔记之逐帧动画&闪烁效果


逐帧动画

在一些场景中,比如页面的加载提示,只使用过渡很难实现预期的效果。在这种情况下,我们可以使用基于图片的逐帧动画来解决。
提到基于图片的逐帧动画首先想到的就是GIF动画。GIF动画在大多数场景下都能满足我们的需求,但是GIF也有一些不足:

  • GIF不具备Alpha透明的特征。但是对于页面的加载提示来说,一般都是半透明的。
  • 低灵活性!我们无法在CSS层面修改动画的某些参数, 比如动画的持续时间、循环次数、是否暂停等。GIF一但生成,这些参数就固定在文件中,如果想要修改就只能重新生成一个GIF文件。

这个时候,可以利用下面这种方式作为补充,我们写一个半透明加载提示的小例子。

首先,把动画的中所有帧全部拼到一张PNG图片中,如下图:

然后用一个容器将图片包裹进去,并把它的宽高设置为单帧的尺寸。
HTML:

<div class="loader">loading...</div>

CSS:

.loader{
    margin: auto;
    width: 100px;
    height: 100px;
    background: url(loader.png);
    overflow: hidden;
    /*隐藏文本*/
    text-indent: 200px;
    white-space: nowrap;
}

这时候只把图片的第一帧显示了出来

然后添加动画效果,修改后的代码如下:

@keyframes loader{
    to {background-position: -800px 0;}
}
.loader{
    margin: auto;
    width: 100px;
    height: 100px;
    background: url(loader.png);
    animation: loader 1s infinite linear;
    overflow: hidden;
    text-indent: 200px;
    white-space: nowrap;
}

刷新下页面:

跟我们预期的效果并不一样。其实只要修改下调速函数就可以了,上文中提到,所有基于贝塞尔曲线的调速函数都是平滑的过渡效果。但是我们现在需求的是逐帧的动画效果。而step()函数会根据指定步进数,把动画切分为多帧,整个动画会在帧与帧之间硬切。
于是修改代码:

animation: loader 1s infinite steps(8);

much nicer!


闪烁效果

在某些论坛或者淘宝店铺的介绍页面,会有闪烁的效果。虽然可以使用GIF来实现,但也可以尝试使用CSS动画来完成闪烁效果。
如果把闪烁拆分为动画,其实就是将原来的颜色逐渐变为透明,然后再逐渐显隐的一个过程。
假设把一段文字闪烁3次。
HTML:

<div class="smooth-blink-a blink">this is a blink test</div>

CSS:

@keyframes blink-1 { to { color:transparent; } }
.blink{
    width: 15em;
    height: 3em;
    margin: 30px auto;
    text-align: center;
    color: black;
    font: 125%/1 sans-serif;
}
.smooth-blink-a{
    animation: 1s blink-1 3;
}

仔细观察会发现,文字平滑的渐变为透明,但是会一下子跳转为原来的颜色。如下图:

为了解决这个问题,可以利用animation-direction,如果属性值是reverse就会反转每一个循环周期,如果是alternate会反转第偶数个循环周期,alternate-reverse会反转第奇数个循环周期。如下图:

如果使用alternate,要实现一次渐隐渐出,就会是两个循环周期(即次数翻倍)。所以也要把animation-duration减半。
修改代码:

.smooth-blink-a{
    animation: .5s blink-1 6;
    animation-direction: alternate;
}

整个动画过渡平滑了许多。

如果不想使用平滑的过渡,想要使用最普通的闪烁效果。瞬间就想到了上节中的step()
修改代码如下:

animation: 1s blink-1  3 steps(1);

刷新页面,发现文本根本不会闪烁。这是由于step(1)等同于step(1,end),这就意味着当前颜色到透明色之间的过渡是在一次步进中完成的,于是颜色的切换只会发生在动画周期的末尾,因此透明色只会在无限短的时间点处出现,在视觉上就是不闪烁。如果设置为step(1,start),那么就会是永久的透明色。

这时候需要扭转下思路,我们可以让转换动作发生在动画的50%处,而不是起始位置。
最终代码如下:

@keyframes blink-2 { 50% {color: transparent;} }
.smooth-blink-a{
    animation: 1s blink-2  3 steps(1);
}

问题解决。