做者:Nicolas(沪江前端开发工程师)
本文原创翻译,转载请注明做者及出处。css
各位,赶忙绑住本身并牢牢抓牢了,由于当你掌握了特别有趣但又复杂的CSS时序函数以后,你将会真正体验到竖起头发般的兴奋感觉。前端
好吧,本文的主题可能还没能让你热血沸腾。言归正传,时序函数对CSS动画而言就像是一颗隐藏的宝石,你想获得多少惊喜取决于你如何使用它。web
首先,让咱们定义下场景,并确保这些与时序函数相关的场景都是咱们熟悉的。如上所述,当你在CSS动画领域中工做时(其中包括CSS过渡和基于关键帧的动画),该功能将变得可用。那么,它到底是什么,它是作什么的呢?数组
它是一个不太显眼的基于动画的CSS属性之一,而它的大多数的相邻属性都是至关自明的。然而,它的特别之处是它使你可以控制和改变更画的加速度 - 也就是说,它定义了动画在指定的持续时间内加速和减速的方式。浏览器
虽然它不影响动画的实际持续时间,但它可能会影响到用户如何感知动画的快或者慢。若是你还不知道它的实际意义,那么请在这里听我娓娓道来,由于时序函数属性比定义的更加有趣。网络
注意:事实上没有确切的“时序函数”的属性命名,当我提到这个“属性”时,既是指transition-timing-function和animation-timing-function属性。架构
在继续以前,让咱们熟悉一下语法以及它适合在CSS中定义整个动画过程的位置。为了浅显易懂,让咱们使用CSS过渡作为例子。咱们将从完整的过渡属性数组开始:函数
div { transition-property: background; transition-duration: 1s; transition-delay: .5s; transition-timing-function: linear; } /* This could, of course, be shortened to: */ div { transition: background 1s .5s linear; }
对定义过渡的简写是至关宽松的,对于顺序的惟一要求是延迟参数必须在持续时间值以后进行声明(但没必要当即跟在后面)。此外,对该功能来讲transition-duration的值是惟一的必填项; 并且因为其余参数的默认值在大多数的时候都会适当填充,所以过渡不多须要超过如下的代码片断:工具
div { transition: 1s; } /* This is the same as saying: */ div { transition: all 1s 0s ease; }
但这有点无趣。虽然默认值一般足以知足页面的标准悬停事件等,但若是你正在作一些更重要的东西,那么时序函数是微调动画的一个重要技巧!字体
无论怎么样,你如今已经有了对时序函数是干什么的基本了解。接下来让咱们来看看它是如何作到的。
揭开面纱
过去不少人可能不会在乎时序函数属性的可用的关键词,它们有五个:ease(默认)ease-in,ease-out,ease-in-out和linear。然而,这些关键字仅仅是用于定义贝塞尔曲线的简写。
纳尼?
你可能不了解这个术语,可是我敢打赌,若是你使用过图形编辑软件,而后你还建立过一条曲线,你实际上看到的是一条贝塞尔曲线!没错,当您使用笔或路径工具来建立一个漂亮的平滑曲线,那么你正在绘制一条贝塞尔曲线!总之,贝塞尔曲线是时序函数背后的黑魔法 ; 它基本描述了在图形上的加速模式。
这是关键字ease的贝塞尔曲线
若是你像我同样第一次看到这样的贝塞尔曲线,那么你可能想知道曲线是怎么从图表上的四个点绘制而成的!我可能没法经过言语来告诉你,但幸运的是,我有一个特别精彩的GIF来帮助我解决这个问题,很是感谢维基百科。
一条正在绘制中的三次方贝塞尔曲线
由于该曲线由四个点造成,咱们将其称为“三次方”贝塞尔曲线,而不是“二次方”曲线(三个点)或“四次方”曲线(五个点)。
那么如今,这才是真正使人感到兴奋的地方了,由于我揭示了你实际上能够经过cubic-bezier()函数简单的替换时序函数属性值的关键字来访问这个曲线。我很是理解你可能须要一些时间来控制你的兴奋情绪...
你可使用cubic-bezier()函数操纵你想要的贝塞尔曲线,从而为你的动画建立出彻底自定义的加速模式!因此,让咱们看看这个函数是如何工做的,以及它是如何使你可以建立出属于本身的贝塞尔曲线的。
首先,咱们知道曲线由四个点造成,称为点0,点1,点2和点3。另一个须要注意的重要事情是,第一个点和最后一个点(0和3)已经在图表上定义了,点0老是位于0,0(左下)和点3老是位于1,1(右上)。
这就使得你用cubic-bezier()函数只能在图表上绘制点1和点2了。此函数传入四个参数,前两个是点1的x和y坐标,后两个是点2的x和y坐标。
transition-timing-function: cubic-bezier(x, y, x, y);
为了温馨的理解语法,以及它是如何建立曲线和动画的物理效果的,我将给你带来5个和时序函数关键字相等的cubic-bezier()值以及动画的最终效果。
让咱们先从ease-in-out关键字开始,由于这条曲线背后的逻辑以及它如何将其转换为动画的多是最容易理解的。
/* The cubic-bezier() equivalent of the ease-in-out keyword */ transition-timing-function: cubic-bezier(.42, 0, .58, 1);
一个彻底对称的贝塞尔曲线,意味着动画从慢速开始到全速,而后再减速。
你能够看到,点1位于沿着x轴的0.42处和在y轴上的0处,而点2位于x轴上的0.58和y轴上的1。这就是一条彻底对称的贝塞尔曲线,意味着动画将低速移动至全速,而后以与起始处彻底相同的速率移出。所以,这个关键字的名称就是这样来的。
若是你看一下下面的演示,你将会看到ease-in-out值的物理效果,以及它和其余关键字的值的不一样之处。CodePen地址。
关键字ease是CSS时序函数属性的默认值,它实际上和前一个很是接近,虽然它以更快的速度移入,而以更平稳的速度移出。
/* The ease keyword and its cubic-bezier() equivalent */ transition-timing-function: cubic-bezier(.25, .1, .25, 1);
关键字ease的曲线以更快的速度移入,以更平稳的速度移出。
你能够看到在动画中的这个时序函数直接转换成的物理效果,在原点处更加陡峭,到结束点又被拉长了。在查看了这些示例后,记得参考以前的演示来对比下效果。
勿庸置疑,关键字ease-in和ease-out是正好相反的。前者是低速开始,然后者是低速结束,其余时间内都保持全速。咱们以前看到的ease-in-out关键字如同逻辑所暗示的那样,就是这2条贝塞尔曲线的完美组合。
/* The ease-in keyword and its cubic-bezier() equivalent */ transition-timing-function: cubic-bezier(.42, 0, 1, 1); /* The ease-out keyword and its cubic-bezier() equivalent */ transition-timing-function: cubic-bezier(0, 0, .58, 1);
ease-in贝塞尔曲线(左)| ease-out贝塞尔曲线(右)
最后一个关键字就彻底不是曲线了。正如其名称所示,linear时序函数值在整个动画过程当中都保持相同的速度,这意味着所获得的贝塞尔曲线(或者根本不是)将只是一条直线。在曲线图上表现为没有变化的加速模式。
/* The linear keyword and its cubic-bezier() equivalent */ transition-timing-function: cubic-bezier(0, 0, 1, 1);
linear时序函数值在整个动画期间保持相同速度。
假如你回到以前的演示,你可能会注意到,尽管全部的例子都有保持不变的持续时间值,但有一部分的动画看起来会慢于其余动画。这是为何呢?就拿ease-in-out做为一个例子,咱们知道,它在开始和结束时都比较慢,这意味着它所涉及动画的中间部分须要以更快的速度执行。这就有效确保了咱们感知的实际动画会更快更短,而线性动画看上去更长。
你也许会以为,这篇文章写的有点拖沓(看我都写了些什么呀?),因此,如今是时候来写点干货了,看看如何使用cubic-bezier()函数来建立自定义的时序函数。
如今咱们已经看到了关键字是如何等同于相对应的贝塞尔曲线的,而且看到了它们在动画上的效果,那么再来看看如何操做曲线来建立自定义加速模式。
如今你应该可以利用cubic-bezier()函数在曲线图上绘制点1和点2 ,并至关清楚这将会如何影响动画。然而,考虑到你一般看不到的曲线图上绘制点,显然这可能会很是无趣。
幸亏还存在Lea Verou这样的牛人,他们彷佛从不须要休息,直到让CSS的开发能够变得更加简单!Lea开发的Cubic Bézier工具,能够用来建立完整的自定义的贝塞尔曲线,并将执行的动做与预约义的关键字进行比较。这意味着,你可使用这个平台工具创建贝塞尔曲线直到获得你想要的效果,而不是在cubic-bezier()函数中无聊地编辑数字。你能够访问Cubic Bezier而后把玩一下曲线,直到能够实现你想要的效果。这就方便多了不是吗。
LEA Verou开发的极其有用的Cubic Bézier(查看大图)
简写的关键字为你提供了在开始使用时序函数时有了很好的选择,可是它们之间的差别一般较小。只有当你开始建立自定义贝塞尔曲线时,你才会意识到时序函数在动画上会给你带来惊人的效果。
请看下面的示例,看一下各个动画在相同持续时间内的极端差别。CodePen地址。
让咱们仔细看一下第一个例子,试着去搞明白为何它会产生这样一个彻底不一样的效果。
/* cubic-bezier() values for first example from preceding demo page */ transition-timing-function: cubic-bezier(.1, .9, .9, .1);
自定义贝塞尔曲线的示例
这个时序函数和默认关键字之间的主要区别是这个陡峭的贝塞尔曲线靠近了“进度”标尺(y轴)。这意味着动画会忽然前进,在中间(即曲线接近水平时)有一段较长的几乎暂停的低速。这种模式与咱们习惯于使用时序函数关键字的方式造成了鲜明对比,它采用相反的方向,缓动时段出如今动画的开始和结束,并非在中间。
如今终于能够将贝塞尔曲线收入囊中了,也已经对这个cubic-bezier()函数的功能属性作了完全的探讨,对吗?也许你是这么想的,但这个狡猾的家伙还有更多的套路能够玩!
没错:贝塞尔曲线还能够更有趣!谁会想到,只有时间标尺(x轴)被限制在曲线图上的0-1的范围内,而进度标尺(y轴)能够继续延伸甚至超出0-1的范围。
进度标尺恰如你所想象的那样,底端(0)标记动画的开始,顶端(1)标记动画的结束。一般,三次的贝塞尔曲线老是以不一样强度在这个进度标尺里向北方向运行,直到到达动画的终点。然而,在0—1范围以外绘制点1和点2的可能性将使得曲线蜿蜒回退到进度标尺内,这实际上会致使在动画中出现反向运动!和以前同样,理解这一点的最好方法是经过视图:
用标准0-1范围以外的值的自定义贝塞尔曲线
你能够看到点2在-0.5的位置,被绘制在标准0-1范围以外,,曲线反向下拉。若是你看下面的演示,你会看到,这会在动画的中段产生一个弹跳效果。CodePen地址。
反之,你能够把反向运动放置于动画的起始处,并故意超出原来终点。它就像是后退了几步又回到了起点; 而后,在终点处,你的运动惯性使你超过了目的地,致使你走了几步后,又保证你回到预期的目的地。请看下面的示例,以完全理解咱们在这里说的什么。另外,产生这种效果的贝塞尔曲线也能够在下面看到。CodePen地址。
使用标准0-1范围以外的值的自定义贝塞尔曲线
你如今应该对cubic-bezier()函数的值在标准的0-1范围之外如何影响到动画播放有一个较好的理解。固然咱们能够成天看移动箱子的例子,但仍是让咱们用一个有创意的时序函数逼真的演示一个示例来完成这一部分的内容。CodePen地址。
没错:这是一个漂浮气球的动画!你不老是想用CSS来作到这样的动画吗?
这个动画的要点是,当你点击“添加氦气”气球飘到了“天花板”, 就像天然世界中的那样,在碰到顶部以前会轻微回弹。使用cubic-bezier()函数的值在0-1范围以外可让咱们创造出弹跳,最终有助于产生一个逼真的效果。下面的代码片断展现了在cubic-bezier()函数中使用的坐标,而最终的贝塞尔曲线的表如今下方能够看到。
/* The cubic-bezier() values for the bouncing balloon */ transition-timing-function: cubic-bezier(.65, 1.95, .03, .32);
模拟弹跳的气球的自定义贝塞尔曲线
这个例子很是好地解释了曲线是如何转换为最终的动画的,由于它表现的几乎完美。首先,能够看到的是,曲线从进度标尺开始一直到结束都是一条直线,表示气球以恒定的速度从动画开始移动到结束。而后,和睦球很是类似的是,曲线从标尺的顶端向下反弹,而后再缓缓地回到顶部。至关简单!
一旦你掌握了曲线并用它来操纵你想要的艺术品,你就成功了。
要注意的最后一点是,当应用于CSS关键帧动画时时序函数是如何表现的。这些概念与咱们迄今为止使用的过渡示例中的概念彻底相同。但须要注意有一个重要的例外:当你应用一个时序函数设置关键帧时,它会在每个关键帧都会被执行,而不是应用于动画的整个部分。
为了证实这一点,假如咱们有四个关键帧,把一个箱子在一个矩形的四个角内移动,而后你应用了“弹跳”时序函数,也就是咱们在前面的气球示例中使用的“反弹”的时序函数,那么四个关键帧中每个动做都会经历反弹,而不是整个动画。让咱们看看这个效果和代码。CodePen地址。
@keyframes square { 25% { top:200px; left:0; } 50% { top:200px; left:400px; } 75% { top:0; left:400px; } } div { animation: square 8s infinite cubic-bezier(.65, 1.95, .03, .32); top: 0; left: 0; /* Other styles */ }
注意,若是100%的关键帧没有定义,那么元素将简单的返回到它起点的样式,这在这个案例中是原本想要的结果,因此就不必定义了。从演示中能够很明显的看到,时序函数应用于四个关键帧中的每个,由于它们每一个都表现为从容器壁反弹。
若是你须要某些关键帧来呈现有别于其余的时序函数,能够直接使用一个单独的时序函数值给到关键帧,好比下面的代码片断。
@keyframes square { 50% { top: 200px; left: 400px; animation-timing-function: ease-in-out; } }
若是你认为这就是时序函数的所有了,那么我告诉你,CSS时序函数要比预约义的缓动函数还要更多!
在这一节中,当咱们经过steps()时序函数探索“分步函数”的概念时,咱们能够把咱们的曲线转换成直线。
steps()函数是一个很小众的工具,但在工具箱中仍然是有用的。它使您可以将动画分红多个步骤,而不是咱们习惯的常规补间动画。例如,若是咱们想要将一个正方形在4秒内分四步向右移动400个像素,那么正方形将每秒向右跳100个像素,而不是连续运动。让咱们来看看在这个特殊案例中所须要的语法,既然咱们已经理解了错综复杂的cubic-bezier()函数,这个就应该很是简单了。CodePen地址。
div { transition: 4s steps(4); } div:target { left: 400px; }
如你所见,将动画分割成多个步骤是如此的简单。但要记住,这个数字必须是一个正整数,因此不能是负数或者小数。然而,第二个可选参数为咱们提供了更多的控制,可能的值有start和end,后者是默认的值。
transition-timing-function: steps(4, start); transition-timing-function: steps(4, end);
Start会在每一个步骤的起始位置运行动画,而end是在每一个步骤的结束处运行动画。使用以前的“移动箱子”示例,下图对二者之间的差别作了很好的解释。
start和end值在steps()函数中的差别。
能够看到,start值,只要动画被触发,它就会当即开始,而end值,它开始于第一个步骤的结尾处(在这个案例中,将会在一秒钟以后被触发)。
为了确保这个概述的足够全面,steps()函数还能够用两个预约义的关键字:step-start和step-end代替。前者至关于steps(1, start),然后者是至关于steps(1, end)。
好吧,你可能没有太多的需求来动画一个移动的箱子,但steps()函数也有不少很酷的用途。例如,若是你有一套基础的卡通的全部图片精灵(sprites),那么你可使用这种技术来逐帧播放,只须要使用几个CSS属性!让咱们来看一个演示和制做它功能的代码。CodePen地址。
div { width: 125px; height: 150px; background: url(images/sprite.jpg) left; transition: 2s steps(16); /* The number of steps = the number of frames in the cartoon */ } div:target { background-position: -2000px 0; }
首先,咱们有一个小的矩形框(125像素宽),它有一个背景图像(2000像素宽),并排包含16帧。这个背景图像最初与框的左边缘对齐; 因此,咱们如今须要作的是将背景图像一直向左移动,以便全部的16帧都经过小矩形窗口。正常动画下,当背景图像向左移动时,帧将仅仅在视图中滑动; 然而,用steps()函数,背景图像能够移动16步到左侧,确保每个16帧的图像能够像你但愿的那样进出视图。就像这样,你仅仅使用CSS过渡就能够播放一个基本的卡通!
这个GIF演示了背景图分步经过“窗口”的概念
我还发现另外一个使用steps()函数的创意来源于LEA Verou,她想出了一个特别巧妙的打字动画。CodePen地址。
首先,你须要一些文本,但不幸的是,你还须要确切的知道你正在使用的字符数,由于你须要在CSS中使用这个数字。另外一个要求是字体必须是等宽的,以便全部字符的宽度彻底相同。
<p>smashingmag</p> .text { width: 6.6em; width: 11ch; /* Number of characters */ border-right: .1em solid; font: 5em monospace; }
咱们正在处理的文本有11个字符。在CSS单位ch的帮助下,咱们实际上可使用这个数字来定义该段的宽度,尽管咱们应该指定回退宽度针对那些并不支持这个单位的浏览器。而后,该段落在右侧显示一个黑色实心框,这将成为光标。如今一切就绪; 咱们只须要简单的使它动起来。
这须要两个单独的动画:一个用于光标,一个用于打字。要实现前者,咱们所须要作的就是使黑色边框闪烁,这没有更简单的了。
@keyframes cursor { 50% { border-color: transparent; } } .text { /* existing styles */ animation: cursor 1s step-end infinite; }
按照原先的设定,黑色边框只会在黑色和透明之间切换,而后连续循环。这是steps()相当重要的地方,由于若是没有它,光标也只是淡入淡出,而不会闪烁。
最后,打字动画也很简单。咱们须要作的是在11个步骤(字符数)中将其宽度从新设置为所有宽度以前,将段落的宽度变为零。
@keyframes typing { from { width: 0; } } .text { /* existing styles */ animation: typing 8s steps(11), cursor 1s step-end infinite; }
在这里有一个关键帧,文本会在8秒内每次显示一个字母,而黑色右边框(光标)会连续闪烁。该技术很是简单,但却颇有效。
只要加上这个由LEA Verou创造的steps()函数的极佳用法,就将彻底改变效果,或许使文字看上去被删除也不在话下。要作到这一点,只需改变下关键帧的关键字,以便它从to读取而不是from,而后添加一个forwards的animation-fill-mode参数到一组动画规则中。这将确保一旦文本“删除”(即当动画完成时),文本仍然保留被删除状态。看一下下面的演示。CodePen地址。
但本节中的两个示例的缺点是,必须事先知道帧或字符的数量,以指定正确的步数,若是此数字变动了,那么您将须要同时更改代码。尽管如此,steps()函数在此已经展示了它的价值,并且是CSS时序函数的另外一个神奇功能。
咱们已经制定的,除非浏览器支持基于CSS的动画,即CSS过渡和CSS动画(基于关键帧的)模块,不然不能使用CSS时序函数。幸运的是,如今浏览器的支持性愈来愈好了。
再次强调,对于关键帧动画,只须要包含-webkit-前缀和没有前缀的代码。
显然,基于CSS动画的浏览器支持性是很是好的,但当涉及到时序函数时,支持性会变得略有不一样。请看下表更详细地说明。
再次再次强调,虽然某些浏览器还须要更长一段时间才能支持时序函数的完整功能,但能够看到目前的浏览器版本对此功能的支持较为广泛。(译者注:目前主流浏览器都已经很好的支持)
让咱们回顾一下,咱们学到了CSS时序函数的什么?。
最后,这不是一篇关于CSS3技术的文章,虽然这些技术如今已经获得全面的支持,但仍是须要作渐进加强。咱们老是要从下到上的处理; 也就是说,渐进加强能够在不能很好支持这些功能的设备和浏览器上为浏览器优化处理,确保你的做品的可接受性和可访问性。
除此以外,祝你在用曲线和分步的时序函数调试动画时玩的愉快!哈~
“Cubic Bézier”,Lea Verou
“Timing Functions”,Mozilla 开发者网络
“Bézier Curves”,维基百科
iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。