如何让你的动画更自然
SabLentz
8年前
<h3>1.为什么需要探究更自然的动画</h3> <p>自css animation推出后,强大的功能使得我们通过css也能制作出媲美flash的动画效果。然而在制作动画的时候,我们也许会常常纠结怎么设置timing-function。一般情况下,我们会直接使用自带的五个动画函数(linear、ease、ease-in、ease-out、ease-in-out),或是在 <a href="/misc/goto?guid=4958824375367626079" rel="nofollow,noindex">cubic-bezier.com</a> 创建一些自定义的动画函数(cubic-bezier(n,n,n,n))。但往往这一切都只是局限于使用,而不知道其原理究竟是什么,以及没有背后的物理原理支撑,使得做出来的动画可能会变得有点形而上学。例如用ease-in来做小球从高处掉下的效果,这个加速效果没有遵循相关物理原理,使得出来的动画效果不太自然。</p> <p>自然的动画效果应该是和我们在现实生活中看到的物体运动轨迹相似的。这样的效果往往与背后的运动曲线函数紧密联系在一起。如上面提到的小球从高处掉下效果,对应的是匀加速运动函数s1=0.5*g*t²。若再探讨之后受到空气阻力及接触面材质影响,回弹的高度s2=s1*n(0<n<1,可以假定n=0.64),如此循环下去,直至小球最后停在地上,这样就可以模拟出整个 <a href="/misc/goto?guid=4959737664531627095" rel="nofollow,noindex">小球掉下效果</a> 。</p> <p>现实生活中的运动效果丰富多样,只靠css3提供的几个基本动画函数是不足以模拟的,例如弹簧动画效果等。要模拟这些真实的效果,就要学会如何获得这些效果背后的动画函数了。</p> <p>下图是用了弹簧曲线效果和只用基本的动画曲线效果的弹窗对比:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/8b2dbc5675f579ddfd737db1d6411b86.gif"></p> <h2>2.探究运动曲线方程</h2> <p>以下以弹簧动画为例,探究一下怎样模拟出这个效果。</p> <p>ios9提供了CASpringAnimation类实现该效果,而web上就没有提供类似函数。但我们仍然可以通过以前学过的物理学和数学知识来做一下研究。</p> <p>下面有一个弹簧块,假设它质量为1,在它不动的时候位置是x = 1,则拉伸时的距离就是x-1了:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/09e20d17acfc39382f806ca0a4b0cae5.jpg"></p> <p>将这比作一个动画,弹簧块在时间t时所处的位置x就可以看作动画曲线函数x = f(t)。如果我们求得这个函数公式,就可以模拟出这个动画效果了。对此,下图将通过物理学公式和数学知识进行探讨。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/ba7db3de53d3c372b2680148f5eba5ae.jpg"></p> <p>在 <a href="/misc/goto?guid=4958203031753610973" rel="nofollow,noindex">Wolfram | Alpha</a> 中输入以上公式后得出</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/65e7d5568e85ab9923058b78ef0ca3c8.png"></p> <p>使用 <a href="/misc/goto?guid=4959737664654564745" rel="nofollow,noindex">工具</a> 绘制函数得:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/c00bb42caeed3849c54fc3e0b384716e.png"></p> <p>感觉还是蛮像一个弹簧曲线的运动轨迹的嘛。像这样,如果我们要模仿自然生活中的某个运动轨迹,可以如上探究一下背后的物理方程,运用数学知识计算,和使用合适的工具,来模拟出对应的运动曲线。但估计很多人都把这些知识还给老师了,因此如果所有曲线都要自己探究的话,就真是太难了。</p> <p>莫怕,后面还有一大半的边幅,就是帮你解决这个问题的。</p> <h2>3.常用的运动曲线</h2> <p>世界上是有很多大神的,他们已经研究出一系列常用的 <a href="/misc/goto?guid=4959660098781606599" rel="nofollow,noindex">动画曲线</a> 了。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/1c1b80e9bf7a923eaa0eaa013d9f6b4d.png"></p> <p>对此做一下简单的介绍:</p> <p>* In和Out:大多数In曲线是从慢到快,可以结合汽车开始跑起来的场景来理解;大多数Out曲线是从快到慢,可以结合汽车慢慢停下来的场景来理解。通常元素飞入时用Out动画,飞出时用In动画,而元素切换时可以用inOut动画(如banner里的图片切换)。如果细心留意一下,你会发现其实Out曲线就是In曲线从右到左运动的轨迹,他们是中心对称的。</p> <p>* Quad : x^2,是一条二次方曲线</p> <p>* Cubic : x^3,是一条三次方曲线</p> <p>* Quart : x^4,是一条四次方曲线</p> <p>* Quint: x^5,是一条五次方曲线</p> <p>* Sine :sin(x^(pi/2))</p> <p>* Expo:2^(10(x-1)),是一个开始非常慢,中后期非常快的曲线</p> <p>* Circ:顾名思义就是弧(1/4圆,如果选择了InOut就是两个外切的1/4圆)</p> <p>* Bounce:这是个模拟小球落地的反弹曲线</p> <p>* elastic:这是个模拟弹簧运动的曲线,就是我们前面研究想得出的曲线</p> <p>这么多曲线,可能大家一看就晕了。我个人理解,用得比较多的应该是其中的几个:</p> <p>1. Quad – x^2:这条二次方曲线,就是匀变速直线运动曲线,大家应该还记得初中背得滚瓜烂熟的s=0.5 * a * t²吧。</p> <p>有了匀变速运动曲线,很多现实中的运动都可以模拟了,如 匀加速运动 、 摩擦力匀减速运动 。如果再组合使用曲线,就能模拟出更多运动了,例如y轴使用二次曲线,x轴使用线性曲线,就模拟出一个 平抛动画 了。</p> <p>2. Cubic – x^3:这是条三次方曲线,大家还记得初中物理哪儿用到这条曲线吗?。。。。对了,就是变加速直线运动,如下图:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/78a620ce11cb0baaf5bd6d423a5537d4.png"></p> <p>在此再附一张上面列举的幂函数曲线对比图供参考和使用:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/0a135c01f8559792e4614d8ac9550e38.png"></p> <p>3.elastic曲线:这个就是前面在研究的弹簧曲线。实现了和ios的spring动画相似的效果。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/df877856ff68c5d09d470dee2a96abc8.gif"></p> <p>4.Bounce曲线:模拟小球落地效果的曲线。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/e1947207deab77bebc6a7c7697fb49db.gif"></p> <p>除此以外,通过用sin曲线设置物体的透明度,可以实现呼吸灯效果。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/a3148f3cfcf44595a8600cb4b2b6028d.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/7ae89fce84ddac70c857789a0c70dbc8.gif"></p> <p>在接下来介绍的GreenSock库中,还有 一些动画曲线 可供使用:</p> <p><img src="https://simg.open-open.com/show/830a510c3412235f6689378d5eef2b1f.jpg"></p> <p>有了这些曲线,我们下一步就是要使用它了,这儿将通过js和css来使用这些曲线。</p> <h2>4.通过js使用动画曲线:</h2> <p> </p> <pre> //部分代码展示 var Tween = { Linear: function(t, b, c, d) { return c*t/d + b; }, Quad: { easeIn: function(t, b, c, d) { return c * (t /= d) * t + b; }, easeOut: function(t, b, c, d) { return -c *(t /= d)*(t-2) + b; }, easeInOut: function(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t + b; return -c / 2 * ((--t) * (t-2) - 1) + b; } }, ……</pre> <p>借助这些函数和requestAnimationFrame,我们可以方便地实现曲线效果,如:</p> <pre> var ball = document.getElementById("ball") var elasticFall = function() { var start = 0, beginingValue = 0, changeValue = 400, during = 100; var _run= function() { start++; var top = Tween.Elastic.easeOut(start, beginingValue, changeValue, during); ball.style.webkitTransform = "translateY("+top+"px)"; if(start < during) requestAnimationFrame(_run); } _run(); }; elasticFall();</pre> <p>接着我们分析一下这些函数怎样使用。大部分的曲线动画都包含4个入参:</p> <p>* t:当前时间</p> <p>* b:初始位置</p> <p>* c: 结束位置</p> <p>* d:运动时间</p> <p>我们主要关心的就是b、c、d,可以理解为物体用了d毫秒从b变成c。这是不是很像设置css动画时要关心的东西呢。而t是给程序获得当前时间,计算出此时间下对应的值。</p> <p>有些动画函数,例如弹簧动画函数Elastic,还有a和p参数。经试验,a:影响振幅,p影响来回次数,按 <a href="/misc/goto?guid=4959737664790311263" rel="nofollow,noindex">这儿</a> 关于ios弹簧动画的描述,a的设置相当于质量,而p相当于阻尼系数。</p> <p>如果不想重复造轮子的话,我搜集了2个动画曲线实现库 <a href="/misc/goto?guid=4959737664860483628" rel="nofollow,noindex">jstween</a> 和 <a href="/misc/goto?guid=4958971398334642667" rel="nofollow,noindex">GreenSock</a> 推荐给大家使用。两个库都是挺容易上手使用的,而且还扩展了很多功能,例如按运动曲线同时改变多个属性、动画播放时或完成时执行回调函数等。</p> <p>以让目标通过 <strong>弹簧效果</strong> 在2秒内从x轴上400像素位置移动到0像素位置(即通过 <strong>弹簧效果</strong> 从屏幕外移到屏幕内)为例,举个栗子:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/df877856ff68c5d09d470dee2a96abc8.gif"></p> <pre> //通过jstween实现 JT.fromTo($target, 2, { x:400 }, { x:0, ease: JT.Elastic.Out, onEnd: function (n) { console.log("animate end.") } }); //通过GreenSock实现 TweenLite.fromTo($target, 2, { x: '400px' }, { x: '0', ease: Elastic.easeOut.config(0.5, 0.4), onComplete:function(){ console.log("animate end.") } } );</pre> <p>在库选用方面考虑,如果想要轻量的,可以选择jstween,只要14k。而GreenSock相对重量一些(最少得引入TweenLite.min.js、EasePack.min.js、CSSPlugin.min.js,共74k),但他提供了更多的运动曲线可供选择,而且还提供其中一些曲线的参数设置,如可以设置弹簧曲线的物体质量和阻尼系数,这是tweenjs所没有的。此外GreenSock还提供了一个 <a href="/misc/goto?guid=4958971398334642667" rel="nofollow,noindex">在线调节参数预览效果的页面</a> 。大家可以根据需要选用合适的库来实现效果。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/8fca889d8fe6273fe104376f69e51fea.png"></p> <h2>5.使用css实现曲线动画效果</h2> <p>我们也可以把这些运动曲线运用到CSS Animation的@keyframes中。以下还是以让目标通过弹簧效果从x轴上400像素位置移动到0像素位置为例,使用Sass来做:</p> <pre> //引入函数库 https://github.com/terkel/mathsass,实现sin,cos等数学函数 @import "node_modules/mathsass/dist/math"; //编写弹簧曲线函数 @function elasticAniFn($t) { @return -0.5 * pow(exp(1), (-6 * $t)) * (-2 * pow(exp(1), (6 * $t)) + sin(12 * $t) + 2 * cos(12 * $t)) } //编写物体位移随时间变化的函数 //$b: 初始值 //$c: 变化量 //$p: 当前运动的进度百分比 //可以理解为物体从$b运动到$c,$p用来表示当前运动了 $p% @function aniFn($b, $c, $p) { @return $b + $p * ($c - $b); } //声明动画 //由此生成的css: //@keyframes moveAni { //0% { // transform: translateX(400px); //} //1% { // transform: translateX(396.54493px); //} //2% { // transform: translateX(386.76446px); //} //3% { // transform: translateX(371.53953px); //} @keyframes moveAni { @for $i from 0 through 100 { {$i}% { transform:translateX(aniFn(400px, 0, elasticAniFn($i / 100))); } } } //使用动画 .box { animation: 1s moveAni linear; transform: rotate }</pre> <h2>6.可视化实现工具介绍</h2> <p>在此我推荐 <a href="/misc/goto?guid=4958542313009830209" rel="nofollow,noindex">Stylie</a> ,一个可视化调节运动曲线且自动生成CSS的工具。</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/3f0a924a2ad930c7ca56a9d60ec945c2.gif"></p> <p>如图所示,左边是动画预览,白色小球会按照设置的曲线不停运动,下方是时间进度条,右边是设置面板。通过可视化地给小球设置每个时间节点上的状态及状态变化时过渡的运动曲线来实现动画效果。</p> <p>对设置面板做一下简单说明:</p> <p>1.第一个0ms处表示开始节点时的状态,第二个1000ms处表示1000ms处时间节点的状态,可以点击它来修改时间。点击右上角的加号可以添加新的时间节点。</p> <p>2.x和y分别表示translateX和translateY,即横坐标及纵坐标,不过一般我会直接拖动左边的绿色十字来调整位置;s表示scale,即缩放倍率;rX、rY、rZ表示rotateX、rotateY、rotateZ,即绕X、Y、Z轴的旋转角度;每个状态右边都可以选择运动曲线,如linear是线性运动曲线,bounce是小球落地的运动曲线。</p> <p>调整满意后就可以导出代码了:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/7a75d40f9b6debda563ae23d8c575958.png"></p> <p>Orient generated animation to是说所有的位移数值采用相对(第一帧的)位移,还是绝对定位(相对于左上角)。class name处可以修改动画的类名,vendors处可以添加需要的浏览器前缀(一般勾选WebKit和W3C就好了)。</p> <p>大概就是这样了,这个工具基本上可以解决很多CSS动画需求了,具体做得怎样就看各人的功力了。</p> <h2>总结</h2> <p>除了基本的css动画函数,我们还可以用更丰富自然的曲线函数去模拟物体的运动。在使用场景上,如果不介意库体积、想有丰富的曲线函数供使用,可以用 GreenSock 来实现;对库体积有要求,可以用 jstween 来实现;觉得只想实现其中一个动画效果而觉得没必要引入整个库,可以只使用其 动画函数 和requestAnimationFrame来实现;不想用js实现(UI开发工程师的骄傲),可以用sass+ 动画函数 来实现;想所见即所得,可以用 Stylie 来实现。而遇到比较特别的动画效果,不能用前面列举的动画函数来实现,就只能通过研究物体运动背后的运动曲线实现了。希望大家看完之后能有所收获,撒花~</p> <p>参考文献:</p> <p>[1]Thai Pangsakulyanont.Spring Animation in CSS</p> <p>[2]阿布evo.可视化CSS3动画生成神器 - Stylie</p> <p>Tags:CSS, 动画 , 曲线 , 弹簧</p> <p> </p> <p>来自:https://isux.tencent.com/native-animation.html</p> <p> </p>