如何讓你的動畫更自然
1.為什么需要探究更自然的動畫
自css animation推出后,強大的功能使得我們通過css也能制作出媲美flash的動畫效果。然而在制作動畫的時候,我們也許會常常糾結怎么設置timing-function。一般情況下,我們會直接使用自帶的五個動畫函數(linear、ease、ease-in、ease-out、ease-in-out),或是在 cubic-bezier.com 創建一些自定義的動畫函數(cubic-bezier(n,n,n,n))。但往往這一切都只是局限于使用,而不知道其原理究竟是什么,以及沒有背后的物理原理支撐,使得做出來的動畫可能會變得有點形而上學。例如用ease-in來做小球從高處掉下的效果,這個加速效果沒有遵循相關物理原理,使得出來的動畫效果不太自然。
自然的動畫效果應該是和我們在現實生活中看到的物體運動軌跡相似的。這樣的效果往往與背后的運動曲線函數緊密聯系在一起。如上面提到的小球從高處掉下效果,對應的是勻加速運動函數s1=0.5*g*t2。若再探討之后受到空氣阻力及接觸面材質影響,回彈的高度s2=s1*n(0<n<1,可以假定n=0.64),如此循環下去,直至小球最后停在地上,這樣就可以模擬出整個 小球掉下效果 。
現實生活中的運動效果豐富多樣,只靠css3提供的幾個基本動畫函數是不足以模擬的,例如彈簧動畫效果等。要模擬這些真實的效果,就要學會如何獲得這些效果背后的動畫函數了。
下圖是用了彈簧曲線效果和只用基本的動畫曲線效果的彈窗對比:
2.探究運動曲線方程
以下以彈簧動畫為例,探究一下怎樣模擬出這個效果。
ios9提供了CASpringAnimation類實現該效果,而web上就沒有提供類似函數。但我們仍然可以通過以前學過的物理學和數學知識來做一下研究。
下面有一個彈簧塊,假設它質量為1,在它不動的時候位置是x = 1,則拉伸時的距離就是x-1了:
將這比作一個動畫,彈簧塊在時間t時所處的位置x就可以看作動畫曲線函數x = f(t)。如果我們求得這個函數公式,就可以模擬出這個動畫效果了。對此,下圖將通過物理學公式和數學知識進行探討。
在 Wolfram | Alpha 中輸入以上公式后得出
使用 工具 繪制函數得:
感覺還是蠻像一個彈簧曲線的運動軌跡的嘛。像這樣,如果我們要模仿自然生活中的某個運動軌跡,可以如上探究一下背后的物理方程,運用數學知識計算,和使用合適的工具,來模擬出對應的運動曲線。但估計很多人都把這些知識還給老師了,因此如果所有曲線都要自己探究的話,就真是太難了。
莫怕,后面還有一大半的邊幅,就是幫你解決這個問題的。
3.常用的運動曲線
世界上是有很多大神的,他們已經研究出一系列常用的 動畫曲線 了。
對此做一下簡單的介紹:
* In和Out:大多數In曲線是從慢到快,可以結合汽車開始跑起來的場景來理解;大多數Out曲線是從快到慢,可以結合汽車慢慢停下來的場景來理解。通常元素飛入時用Out動畫,飛出時用In動畫,而元素切換時可以用inOut動畫(如banner里的圖片切換)。如果細心留意一下,你會發現其實Out曲線就是In曲線從右到左運動的軌跡,他們是中心對稱的。
* Quad : x^2,是一條二次方曲線
* Cubic : x^3,是一條三次方曲線
* Quart : x^4,是一條四次方曲線
* Quint: x^5,是一條五次方曲線
* Sine :sin(x^(pi/2))
* Expo:2^(10(x-1)),是一個開始非常慢,中后期非常快的曲線
* Circ:顧名思義就是弧(1/4圓,如果選擇了InOut就是兩個外切的1/4圓)
* Bounce:這是個模擬小球落地的反彈曲線
* elastic:這是個模擬彈簧運動的曲線,就是我們前面研究想得出的曲線
這么多曲線,可能大家一看就暈了。我個人理解,用得比較多的應該是其中的幾個:
1. Quad – x^2:這條二次方曲線,就是勻變速直線運動曲線,大家應該還記得初中背得滾瓜爛熟的s=0.5 * a * t2吧。
有了勻變速運動曲線,很多現實中的運動都可以模擬了,如 勻加速運動 、 摩擦力勻減速運動 。如果再組合使用曲線,就能模擬出更多運動了,例如y軸使用二次曲線,x軸使用線性曲線,就模擬出一個 平拋動畫 了。
2. Cubic – x^3:這是條三次方曲線,大家還記得初中物理哪兒用到這條曲線嗎?。。。。對了,就是變加速直線運動,如下圖:
在此再附一張上面列舉的冪函數曲線對比圖供參考和使用:
3.elastic曲線:這個就是前面在研究的彈簧曲線。實現了和ios的spring動畫相似的效果。
4.Bounce曲線:模擬小球落地效果的曲線。
除此以外,通過用sin曲線設置物體的透明度,可以實現呼吸燈效果。
在接下來介紹的GreenSock庫中,還有 一些動畫曲線 可供使用:
有了這些曲線,我們下一步就是要使用它了,這兒將通過js和css來使用這些曲線。
4.通過js使用動畫曲線:
//部分代碼展示 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; } }, ……
借助這些函數和requestAnimationFrame,我們可以方便地實現曲線效果,如:
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();
接著我們分析一下這些函數怎樣使用。大部分的曲線動畫都包含4個入參:
* t:當前時間
* b:初始位置
* c: 結束位置
* d:運動時間
我們主要關心的就是b、c、d,可以理解為物體用了d毫秒從b變成c。這是不是很像設置css動畫時要關心的東西呢。而t是給程序獲得當前時間,計算出此時間下對應的值。
有些動畫函數,例如彈簧動畫函數Elastic,還有a和p參數。經試驗,a:影響振幅,p影響來回次數,按 這兒 關于ios彈簧動畫的描述,a的設置相當于質量,而p相當于阻尼系數。
如果不想重復造輪子的話,我搜集了2個動畫曲線實現庫 jstween 和 GreenSock 推薦給大家使用。兩個庫都是挺容易上手使用的,而且還擴展了很多功能,例如按運動曲線同時改變多個屬性、動畫播放時或完成時執行回調函數等。
以讓目標通過 彈簧效果 在2秒內從x軸上400像素位置移動到0像素位置(即通過 彈簧效果 從屏幕外移到屏幕內)為例,舉個栗子:
//通過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>
在庫選用方面考慮,如果想要輕量的,可以選擇jstween,只要14k。而GreenSock相對重量一些(最少得引入TweenLite.min.js、EasePack.min.js、CSSPlugin.min.js,共74k),但他提供了更多的運動曲線可供選擇,而且還提供其中一些曲線的參數設置,如可以設置彈簧曲線的物體質量和阻尼系數,這是tweenjs所沒有的。此外GreenSock還提供了一個 在線調節參數預覽效果的頁面 。大家可以根據需要選用合適的庫來實現效果。
5.使用css實現曲線動畫效果
我們也可以把這些運動曲線運用到CSS Animation的@keyframes中。以下還是以讓目標通過彈簧效果從x軸上400像素位置移動到0像素位置為例,使用Sass來做:
//引入函數庫 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>
6.可視化實現工具介紹
在此我推薦 Stylie ,一個可視化調節運動曲線且自動生成CSS的工具。
如圖所示,左邊是動畫預覽,白色小球會按照設置的曲線不停運動,下方是時間進度條,右邊是設置面板。通過可視化地給小球設置每個時間節點上的狀態及狀態變化時過渡的運動曲線來實現動畫效果。
對設置面板做一下簡單說明:
1.第一個0ms處表示開始節點時的狀態,第二個1000ms處表示1000ms處時間節點的狀態,可以點擊它來修改時間。點擊右上角的加號可以添加新的時間節點。
2.x和y分別表示translateX和translateY,即橫坐標及縱坐標,不過一般我會直接拖動左邊的綠色十字來調整位置;s表示scale,即縮放倍率;rX、rY、rZ表示rotateX、rotateY、rotateZ,即繞X、Y、Z軸的旋轉角度;每個狀態右邊都可以選擇運動曲線,如linear是線性運動曲線,bounce是小球落地的運動曲線。
調整滿意后就可以導出代碼了:
Orient generated animation to是說所有的位移數值采用相對(第一幀的)位移,還是絕對定位(相對于左上角)。class name處可以修改動畫的類名,vendors處可以添加需要的瀏覽器前綴(一般勾選WebKit和W3C就好了)。
大概就是這樣了,這個工具基本上可以解決很多CSS動畫需求了,具體做得怎樣就看各人的功力了。
總結
除了基本的css動畫函數,我們還可以用更豐富自然的曲線函數去模擬物體的運動。在使用場景上,如果不介意庫體積、想有豐富的曲線函數供使用,可以用 GreenSock 來實現;對庫體積有要求,可以用 jstween 來實現;覺得只想實現其中一個動畫效果而覺得沒必要引入整個庫,可以只使用其 動畫函數 和requestAnimationFrame來實現;不想用js實現(UI開發工程師的驕傲),可以用sass+ 動畫函數 來實現;想所見即所得,可以用 Stylie 來實現。而遇到比較特別的動畫效果,不能用前面列舉的動畫函數來實現,就只能通過研究物體運動背后的運動曲線實現了。希望大家看完之后能有所收獲,撒花~
參考文獻:
[1]Thai Pangsakulyanont.Spring Animation in CSS
[2]阿布evo.可視化CSS3動畫生成神器 - Stylie
Tags:CSS, 動畫 , 曲線 , 彈簧
來自:https://isux.tencent.com/native-animation.html