在上一篇《基於HT for Web向量實現3D葉輪旋轉》一文中,我略微提了下HT for Web基礎動畫的相關用法,但是講得不深入,今天就來和大家分享下HT for Web基礎動畫的相關介紹及用法。
先上一段枯燥的理論知識,大家混個眼熟。
在HT的資料模型驅動圖形元件的設計架構下,動畫可理解為將某些屬性由起始值逐漸變到目標值的過程, HT提供了ht.Default.startAnim的動畫函式,其示例程式碼如下。
ht.Default.startAnim({ frames: 12, // 動畫幀數 interval: 10, // 動畫幀間隔毫秒數 easing: function(t){ return t * t; }, // 動畫緩動函式,預設採用`ht.Default.animEasing` finishFunc: function(){ console.log('Done!') }, // 動畫結束後呼叫的函式。 action: function(v, t){ // action函式必須提供,實現動畫過程中的屬性變化。 node.setPosition( // 此例子展示將節點`node`從位置`p1`動畫到位置`p2`。 p1.x + (p2.x - p1.x) * v, p1.y + (p2.y - p1.y) * v ); } });
ht.Default.startAnim支援Frame-Based和Time-Based兩種方式的動畫,以上程式碼為Frame-Based方式, 這種方式使用者通過指定frames動畫幀數,以及interval動畫幀間隔引數控制動畫效果。
以下程式碼為Time-Based方式,該方式使用者只需要指定duration的動畫週期的毫秒數即可,HT將在指定的時間週期內完成動畫, 不同於Frame-Based方式有明確固定的幀數,即action函式被呼叫多少次,Time-Based方式幀數或action函式被呼叫次數取決於系統環境, 一般來說系統配置更好的機器,更高效的瀏覽器則呼叫幀數越多,動畫過程更平滑。由於js語言無法精確控制interval時間間隔, 採用Frame-Based不能精確控制動畫時間週期,即使相同的frames和interval引數在不同的環境,可能會出現動畫週期差異較大的問題, 因此HT預設採用Time-Based的方式,如果不設定duration和frames引數,則duration引數將被系統自動設定為ht.Default.animDuration值。
ht.Default.startAnim({ duration: 500, // 動畫週期毫秒數,預設採用`ht.Default.animDuration` action: function(v, t){ ... } });
startAnim函式會返回一個anim物件,可呼叫anim.stop(true)終止動畫,其中的引數shouldBeFinished代表是否完全未達到的目標改變, 如果為true則會呼叫anim.action(anim.easing(1))。同時anim還具有anim.pause()和anim.resume()可中斷和繼續動畫功能, 以及anim.isRunning()函式判斷動畫是否正在進行。
action函式的第一個引數v代表通過easing(t)函式運算後的值,t代表當前動畫進行的進度[0~1],一般屬性變化根據v引數進行。
ht.Default.startAnim中得easing引數是用於讓使用者定義函式,通過數學公式控制動畫, 如勻速變化、先慢後快等效果,可參考http://easings.net/
介紹就到這裡,接下來我們來做一個簡單的例子,先練練手,具體的頁面效果如下:
沒錯。它就是一個球,我們要做的就是點選瀏覽器的某個位置,然後它平滑地滑到點選位置,點選自身的話,就做旋轉收縮,然後再旋轉還原,整個過程都是通過HT for Web的基礎動畫來完成的。
建立拓撲及建立節點的程式碼我再這裡就不多囉嗦了,我們直接上基礎動畫的內容:
首先我們需要在拓撲上監聽點選事件,因為我們的Demo涉及到兩個動畫,所以在點選事件的內部處理需要做些簡單的判斷,我們先來看下監聽改如何新增:
var type = "ontouchend" in document ? 'touchstart' : 'mousedown'; view.addEventListener(type, function(e){ e.preventDefault(); … }, false); 以下是圖元的平移動畫的處理: var p2 = graphView.getLogicalPoint(e); var p1 = toy.getPosition(); anim = ht.Default.startAnim({ duration: 500, easing: easing, finishFunc: finishFunc, action: function(v){ toy.setPosition( p1.x + (p2.x - p1.x) * v, p1.y + (p2.y - p1.y) * v ); } });
通過事件物件獲取到當前點選的位置,作為終點,然後獲取圖元的位置座標作為起點,然後通過ht.Default.startAnim()方法建立一個基礎動畫,在action函式內部不斷地改變圖元的position位置屬性,令其從起點運動到終點,整個過程歷時500毫秒。
接下來看下自身動畫該如何設計:
var size = toy.getSize(); ht.Default.startAnim({ frames: 30, interval: 16, easing: easing, finishFunc: finishFunc, action: function(v){ toy.setRotation(Math.PI * v); var r = Math.abs(v - 0.5) * 2; toy.setSize(size.width * r, size.height * r); } });
點選圖元時觸發圖元圍繞自身中心旋轉一週,同時圖元由大變小再恢復原尺寸,該邏輯通過設定frames為30幀和interval為16毫秒間隔的 Frame-Based方式完成動畫。
我們兩個動畫都有了,那麼接下來就是將兩個動畫拼接到一起,在監聽裡面該如何出來才能將連個動畫串接起來呢?其實很簡單,我們只需要判斷當前點選的物件就可以很容易分辨出到底要播放哪個動畫,以下的程式碼就是具體的點選事件邏輯處理:
if(isAnimating || !ht.Default.isLeftButton(e)){ return; } isAnimating = true; var data = graphView.getDataAt(e); var easing = function (t) { return ( 2 - t) * t; }; var finishFunc = function(){ isAnimating = false; }; if(data === toy){ // 自身動畫 … }else{ // 平移動畫 … }
這個Demo到這裡就算結束了,這個Demo是在2D上的應用,接下來我們來看一個3D上的應用
本次要設計的3D應用是一個在頁面初始化後,圖元從遠到近呈現在螢幕上,然後緩慢地做360度的旋轉,令圖元的各個視角都呈現在眼前。
哈哈,飛機又和大家見面了,在上一章節的Demo中,載入obj檔案完成操作後的finishFunc函式中新增以下程式碼:
fromEye = [0, 900*5, 1200*5]; toEye = [0, 100, 600]; g3d.setEye(fromEye); ht.Default.startAnim({ duration: 1000, action: function(t){ g3d.setEye([ fromEye[0] + (toEye[0] - fromEye[0]) * t, fromEye[1] + (toEye[1] - fromEye[1]) * t, fromEye[2] + (toEye[2] - fromEye[2]) * t ]); }, finishFunc: function(){ setInterval(function(){ g3d.rotate(Math.PI/360, 0); }, 30); } });
程式碼的邏輯也很簡單,通過設定視角就能夠實現圖元由遠到近的感覺,當圖元呈現在眼前後,我們通過定時器旋轉拓撲元件,令圖元水平360度呈現。
在這個例子中,我並沒有操作圖元的屬性值,都是在操作拓撲的屬性值,所以效果的呈現有可能會有多種實現方式,關鍵是要懂得思考和運用,那麼這個飛機的Demo,通過直接改變圖元屬性來達到以上相同效果該如何實現呢?感興趣的朋友不妨嘗試下。
以下是本次介紹涉及到的Demo原始碼及相關視訊:
http://v.youku.com/v_show/id_XMTI5OTg5ODE1Ng==.html