在前端实现元素的动画须要用到transition或者animation搭配transform作位移的效果,但这只能实现元素沿着直线运动。若是想要让元素沿着曲线运动,则须要用到本文介绍的“分层动画”。javascript
你们还记得高中物理的“平抛运动”吗?运动轨迹为一条曲线,它能够当作是水平方向上的匀速运动和垂直方向上的自由落体的组合。更进一步,运动轨迹为曲线必须在水平与垂直两个方向上有不一样的以时间为自变量的路径函数f(t)。css
以此类推到前端,过渡与动画中的属性time-function就是以时间为自变量的路径函数f(t),只要让元素在水平和垂直方向上有不一样的time-function,就可实现曲线运动的效果。接着咱们将使用分层动画法,让元素同时沿着不一样方向运动。html
分层动画法,就是将运动曲线分解为水平与垂直的运动,元素自己只执行一个方向的运动,其外层容器执行另外一方向上的运动。前端
HTML结构以下:java
<div class="container"> <div class="dot"></div> </div>
让container作水平运动,dot作垂直运动,注意二者使用的缓动函数要是不一样的,CSS代码以下:git
.container { /* ... 定位等 */ animation: horizontal 2s infinite ease-in; } .dot { /* ... 定位等 */ animation: vertical 2s infinite ease; } @keyframes horizontal { 100% { transform: translateX(150px); } } @keyframes vertical { 50% { animation-timing-function: ease-in; transform: translateY(-100px); } 100% { transform: translateY(0); } }
实现的效果就是简单的抛物线,以下动图:github
上例中用到的缓动函数ease和ease-in比较简单,除了简单的缓动函数以外,还能够用贝塞尔曲线函数实现更复杂的曲线运动。算法
大三图形学的时候接触过贝塞尔曲线,后来因为没有实际应用场景,勉强只能模糊的记得公式的样子。现在,贝塞尔曲线在前端领域中也占有一席之地,有了应用场景,学习起来会更有目的性也能理解得更深入。前端工程师
首先,咱们须要知道贝塞尔曲线是如何绘制。网上绘制贝塞尔曲线的资料不少,这里推荐参考文献[1]中的扫盲文,介绍的是德卡斯特里奥(de Casteljau)算法,通俗易懂,步骤详实。函数
例如,参照下图,ABC三个点做为控制点绘制曲线的大体步骤是:
肯定三个辅助点DEF,使其始终知足以下比例关系:
AD:AB = BE:BC = DF:DE
而后将AD:AB的值从0取到1,全部的F所组成的曲线就是ABC三个控制点的二阶贝塞尔曲线。
动图比较直观一点:
前端领域中动画或过渡的缓动函数就是三阶贝塞尔曲线,即四个控制点的贝塞尔曲线,以下图所示。
若是以为比较抽象,能够用这个网址 http://myst729.github.io/bezi... ,画四个点观察绘制过程。
图中有四个控制点,左下方和右上方两个点的位置固定,其坐标为(0,0)
与(1,1)
。另两个点可移动,坐标分别为红点记为(x1, y1)
,绿点记为(x2, y2)
。缓动函数中贝塞尔函数是cubic-bezier(x1, y1, x2, y2)
,四个参数就是中间两个控制点的坐标。
分层动画与贝塞尔曲线联合可以实现一些酷炫的效果,只是我暂时没有找到一个能够精细量化曲线动画的方法,只能粗略肯定贝塞尔函数并在细微处调整。
以参考文献[2]中的动画为例,以下图的“what we want”
例子中的是对称循环的动画,咱们先分析完整动画的一半。
首先看水平方向,位移是translateX(200px),小黑球水平移动的速率是先快后慢,因此缓动函数应该是以下图的贝塞尔曲线:
再来看垂直方向,位移是translateY(-200px),小黑球垂直移动的速率是先缓慢出发,忽然快速前进超过终点再缓慢往回,因此缓动函数应该是以下图的贝塞尔曲线:
总体的CSS代码以下:
.container.special { animation: xAxis 2s infinite cubic-bezier(.02,.01,.26,1); } .dot { animation: yAxis 2s infinite cubic-bezier(.29,.27,.08,1.58); } @keyframes xAxis { 50% { transform: translateX(150px); } } @keyframes yAxis { 50% { transform: translateY(-150px); } }
效果以下图:
能够看出,前一半的动画(左下到右上的过程)与预期实现基本一致。接下来就是处理后一半,后一半的动画就是前一半的镜象对称,也就是前一半的水平方向运动与后一半的垂直方向运动相同,前一半的垂直方向运动与后一半的水平方向运动相同。
.container.special { animation: xAxis 2s infinite cubic-bezier(.02,.01,.26,1); } .dot { animation: yAxis 2s infinite cubic-bezier(.29,.27,.08,1.58); } @keyframes xAxis { 50% { animation-timing-function: cubic-bezier(.29,.27,.08,1.58); /* 后一半用前一半垂直向上的缓动函数 */ transform: translateX(150px); } } @keyframes yAxis { 50% { animation-timing-function: cubic-bezier(.02,.01,.26,1); /* 后一半用前一半水平方向上的缓动函数 */ transform: translateY(-150px); } }
效果以下图: