现代 CSS

CSS秘密花园: 逐帧动画

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

CSS Secrets》是@Lea Verou最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。

CSS Secrets

问题

通常,我们需要一个动画效果是比较难的或者说使用CSS的transition是不可能实现的。例如,一个卡通移动或一个复杂的进度条。在这种情况下,基于图像的逐帧动画是完美的,但是实现这种效果是具有挑战性的。

在这一点上,你可能会说:难道我们不能使用gif动画?很多情况下,答案是肯定的,gif动画是完美的。然而,他们也有一些缺点,可能在一些地方无法使用:

  • 所有帧中被限制使用256色调色板
  • 他们不能具有透明度,这是一个很大的问题。例如,下图所示的效果是很常见的。
  • 在CSS中有些方面无法做修改,比如时间、重复和停顿等等。一旦生成gif文件,一切都存在文件中,只能通过一个图像编辑器来做修改或生成另一个文件。

逐帧动画

上图是一个半透明的进度指示器(dabblet.com),这个是Gif图无法实现的效果。

早在2004年,Mozilla团队就解决了前两个问题,他们通过逐帧动画PNG文件,实现类似静态的方式或者动画的GIF文件。他们把这种设计称为APNG。但是,直到今天,支持APNG的浏览器和制图软件都非常有限。

开发人员甚至通过使用一个Sprite图和使用JavaScript脚本来动态更改background-position值,在浏览器中实现灵活的逐帧动画。你甚至可以找到一个小的库来完成,那么有没有一个简单的方法,使用CSS来实现这样的效果呢?

解决方案

假设我们所有帧动画用到的Sprites的PNG图如下所示:

逐帧动画

我们有一个元素,用来制作加载动画(别忘了,在元素里面放些文本,提搞可访性!),元素大小是单帧动画的尺寸:

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

.loader {
    width: 100px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    text-indent: 999px;
    overflow: hidden;
    background: url(http://www.w3cplus.com/sites/default/files/blogs/2015/1507/loader.png) 0 0;
}

目前结果如下图所示:

逐帧动画

将显示的是第一帧,但是没有动画效果。如果我们对background-position进行不同值的设置,比如-100px,0是第二帧,-200px 0是第三帧等等。这样一来,首先想到的他可能是一个动画:

@keyframes loader {
    to { background-position: -800px 0; }
}
.loader {
    animation: loader 1s infinite linear;
    /*省略其他样式*/
}

然而,正如下图所看到的图片(每167ms),这并不是真正需要的效果:

逐帧动画

当我们不需要关键帧之间的插值时,我们最初尝试的逐帧动画效果失败了。

虽然我们还没有实现逐帧动画效果,但实际上我们已经非常接近解决方案。这里有一个秘密,将使用steps()时间函数来替代前面介绍的“Bézier-based”时间函数。

你可能会问,这是什么时间函数?正如上一节介绍的,我们看到的都是有关于“Bézier-based”时间函数来控制关键帧之间的平滑过渡。通常情况之下,在transitionanimation都可以实现平滑过渡。然而,在这种情况这下,这种平滑过渡时间函数直接摧毁了我们使用Sprite制作的动画。

steps()时间函数和“Bézier-based”很不一样,他可以根据整个动画的关键帧步骤指定步骤的数量,并且可以对插值进行开关的设置。在CSS中,有关于steps()讨论不太多。至于CSS中有关于时间函数功能,“Bézier-based”才是亲妈的生,很受欢迎,但steps()是后妈的,一直不受人待。然而,在这种情况下,steps()才正是我们需要的。一旦把这个用到加载的动画中,动画才变得正常,也是我们需要的效果:

animation: loader 1s infinite steps(8);

请记住,steps()还可以接受第二个参数,而且这个参数是可选的。startend(默认的),用来设定每个时间间隔开关,如下图所示:

逐帧动画

如果我们只需要一个步骤,也可以通过step-startstep-end快捷键,其相当于steps(1,start)steps(1,end)

提示:Simurai给Sprites动画提供了一个使用steps()技术

如需转载,烦请注明出处:http://www.w3cplus.com/css3/css-secrets/frame-by-frame-animations.htmlDiamond Supply PUMA Black Friday

返回顶部