jQuery動畫是經過animate這個API設置執行的,其內部也是按照每個animate的劃分封裝了各自動畫組的行爲,javascript
包括數據過濾、緩動公式、一些動畫默認參數的設置、元素狀態的調整、事件的處理通知機制、執行等等css
換句話說,咱們能夠把animate看做一個對象,對象封裝本身的一系列屬性與方法。html
jQuery能夠支持連續動畫,那麼animate與animate之間的切換就是經過隊列.queue,這個以前就已經詳細的解釋過了java
jQuery的內部的方法都是針對API的處理範圍設計的jquery
咱們看看Animation方法的支持狀況:ajax
.animate( properties [, duration ] [, easing ] [, complete ] )
.animate( properties, options )
width
, height
或者left
能夠執行動畫,可是background-color
不能,可是也不是絕對的,主要看數據的解析度,能夠用插件支持scrollTop
和 scrollLeft
,以及自定義屬性,也可應用於動畫'show'
, 'hide'
, 和 'toggle'
。這些快捷方式容許定製隱藏和顯示動畫用來控制元素的顯示或隱藏。爲了使用jQuery內置的切換狀態跟蹤,'toggle'
關鍵字必須在動畫開始前給定屬性值簡單的來講,就是把一對的參數丟大animate方法裏面,而後animate就開始執行你參數規定的動畫了,算法
那麼動畫每執一次就會經過回調通知告訴開發者,具體有complete/done/fail/always/step接口等等promise
<img id="book" alt="" width="100" height="123" style="background:red;opacity:1;position: relative; left: 500px;" /> book.animate({ opacity: 0.25, left: '50', height: 'toggle' }, { duration :1000, specialEasing: { height: 'linear' }, step: function(now, fx) { console.log('step') }, progress:function(){ console.log('progress') }, complete:function(){ console.log('動畫完成') } })
首先,動畫的參數都是最終值都是相對數據瀏覽器
如上img元素的起始dom
opacity是1,那麼經過動畫改爲成0.25
left是500,那麼經過動畫改爲成50
height爲'toggle' 意味着若是是隱藏與顯示的自動切換
step:是針對opacity/left/height各自動畫,每次改變通知三次
progress 是把opacity/left/height當作一組了,每次改變只通知一次
jQuery動畫的原理仍是很簡單的,靠定時器不斷的改變元素的屬性
咱們模擬下animate的大體實現
讓元素執行一個2秒的動畫到座標爲left 50的區域
animate({
left: '50', duration: '2000' }
按照常規的思路,咱們須要3個參數
思路一:等值變化
咱們在animate內部還須要計算出固然元素的初始化佈局的位置(好比500px),那麼咱們在2秒的時間內需變換成50px,也就是運行的路勁長就是500-50 = 450px
那麼算法是否是呼之欲出了?
每毫秒移動的距離 pos = 450/2000 = 0.225px
每毫秒移動left = 初始位置 (+/-) 每毫秒遞增的距離(pos * 時間)
這樣算法咱們放到setInterval就會發現錯的一塌糊塗,咱們錯最本質的東西:JS是單線程,定時器都是排隊列的,理論上也達不到1ms繪製一次dom
因此每次產生的這個下一次繪製的時間差根本不是一個等比的,因此咱們按照線性的等值遞增是有誤的
function animate(elem, options){ //動畫初始值 var start = 500 //動畫結束值 var end = options.left //動畫id var timerId; var createTime = function(){ return (+new Date) } var startTime = createTime(); //須要執行動畫的長度 var anminLength = start - end; //每13毫秒要跑的位置 var pos = anminLength/options.time * 13 var pre = start; var newValue; function tick(){ if(createTime() - startTime < options.time){ newValue = pre - pos //動畫執行 elem.style['left'] = newValue + 'px'; pre = newValue }else{ //中止動畫 clearInterval(timerId); timerId = null; console.log(newValue) } } //開始執行動畫 var timerId = setInterval(tick, 13); }
思路一實現:
思路二:動態計算
setInterval的調用是不規律的,可是調用的時間是(2秒)是固定的,咱們能夠在每次調用的時候算法時間差的比值,用這個比值去計算移動的距離就比較準確了
remaining = Math.max(0, startTime + duration - currentTime),
經過這個公司咱們計算出,每次setInterval調用的時候,當前時間在總時間中的一個位置
remaining
看到沒有,這個值其實很符合定時器的特性,也是一個沒有規律的值
根據這個值,咱們能夠得出當前位置的一個百分比了
var remaining = Math.max(0, startTime + options.duration - createTime()) var temp = remaining / options.duration || 0; var percent = 1 - temp;
pecent
那麼這個移動的距離就很簡單了
我把整個公式就直接列出來了
var createTime = function(){ return (+new Date) } //元素初始化位置 var startLeft = 500; //元素終點位置 var endLeft = 50; //動畫運行時間 var duration = 2000; //動畫開始時間 var startTime = createTime(); function tick(){ //每次變化的時間 var remaining = Math.max(0, startTime + duration - createTime()) var temp = remaining / duration || 0; var percent = 1 - temp; //最終每次移動的left距離 var leftPos = (endLeft- startLeft) * percent +startLeft; } //開始執行動畫 setInterval(tick, 13);
leftPos就是每次移動的距離了,基本上比較準確了,事實上jQuery內部也就是這麼幹的
這裏13表明了動畫每秒運行幀數,默認是13毫秒。屬性值越小,在速度較快的瀏覽器中(例如,Chrome),動畫執行的越流暢,可是會影響程序的性能而且佔用更多的 CPU 資源
在新的遊覽器中,咱們均可以採用requestAnimationFrame更優
思路二實現:
知道動畫處理的基本原理與算法了,那麼jQuery在這個基礎上封裝擴展,讓動畫使用起來更靈活方便
我概括有幾點:
基於promise的事件通知
得益於deferred的機制,可讓一個對象轉化成帶有promise的特性,實現了done/fail/always/progress等等一系列的事件反饋接口
這樣的設計咱們並不陌生在ready、ajax包括動畫都是基於這樣的異步模型的結構
deferred = jQuery.Deferred() //生成一個動畫對象了 animation = deferred.promise({}) //混入動畫的屬性與方法
那麼這樣操做的一個好處就是,能夠把邏輯處理都放到一塊
咱們在代碼的某一個環節針對特別的處理,須要臨時改變一些東西,可是在以後咱們但願又恢復原樣,爲了邏輯的清晰,咱們能夠引入deferred.alway方法
在某一個環節改了一個屬性,而後註冊到alway方法上一個完成的回調用來恢復,這樣的邏輯塊是很清晰的
style.overflow = "hidden"; anim.always(function() { //完成後恢復溢出 style.overflow = opts.overflow[0]; style.overflowX = opts.overflow[1]; style.overflowY = opts.overflow[2]; });
增長屬性的show/hide/toggle的快捷方式
指定中文參數是比較特殊的,這種方式也是jQuery本身擴展的行爲,邏輯上也很容易處理
ook.animate({ left: '50', height:'hide' },
height高度在動畫結束以後隱藏元素,那麼意味着元素自己的高度height也是須要改變的從初始的位置慢慢的遞減到0而後隱藏起來
代碼中有這麼一段,針對hide的動做,咱們在done以後會給元素直接隱藏起來
//目標是顯示 if (hidden) { jQuery(elem).show(); } else { //目標是隱藏 anim.done(function() { jQuery(elem).hide(); }); }
其實show與hide的流程是同樣的,只是針對元素在初始與結束的一個狀態的改變
css屬性設置獨立的緩動函數
在動畫預初始化以後(爲了支持動畫,臨時改變元素的一些屬性與狀態),咱們就須要給每個屬性生成一個獨立的緩動對象了createTween,主要用於封裝這個動畫的算法與執行的一些流程操做控制
////////////////// //生成對應的緩動動畫 // ////////////////// createTween: function(prop, end) { var tween = jQuery.Tween(elem, animation.opts, prop, end, animation.opts.specialEasing[prop] || animation.opts.easing); //加入到緩動隊列 animation.tweens.push(tween); return tween; },
tween對象
經過這個結構大概就知道了,這個就是用於生成動畫算法所須要的一些數據與算法的具體流程控制了
咱們知道元素自己在佈局的時候能夠用不少屬性對其設置,但是一旦進行動畫化的話,某些屬性的設置可能會對動畫的執行產生反作用,因此針對這樣的屬性,jQuery直接在內部作了最優的處理
若是咱們進行元素height/width變化的時候,好比height:1,這樣的處理jQuery就須要針對元素作一些強制性的處理
1 添加overflow =「hidden」
2.若是設置了內聯而且沒有設置浮動 display = "inline-block";
由於內容溢出與內聯元素在執行動畫的時候,與這個height/width的邏輯是符合的
固然針對這樣的修改jQuery很是巧妙了用到了deferred.always方法,咱們在執行動畫的時候,因爲動畫的須要改了原始的屬性,可是動畫在結束以後,咱們仍是須要還原成其狀態
deferred量身定作的always方法,無論成功與失敗都會執行這個復原的邏輯
//設置溢出隱藏 if (opts.overflow) { style.overflow = "hidden"; anim.always(function() { //完成後恢復溢出 style.overflow = opts.overflow[0]; style.overflowX = opts.overflow[1]; style.overflowY = opts.overflow[2]; }); }
經過上面不難看出,jQuery動畫其實原理上自己是不復雜的。量變產生質變,經過擴展大量的便捷方式加大了邏輯上的難度,可是從根本上來講:
主要包括:
大致上jQuery的動畫就這麼些內容,固然還有一些細節的話 遇到在提出來了,下章就會經過上面的這些處理,實現一個類jquery動畫的模擬了,增強理解