Jquery中文網 www.4271936.live
Jquery中文網 >  jQuery  >  jquery 教程  >  正文 jQuery源碼分析系列(40): 動畫設計

jQuery源碼分析系列(40): 動畫設計

發布時間:2016-09-17   編輯:www.4271936.live
jquery中文網為您提供jQuery源碼分析系列(40): 動畫設計等資源,歡迎您收藏本站,我們將為您提供最新的jQuery源碼分析系列(40): 動畫設計資源

前言

jquery動畫是通過animate這個API設置執行的,其內部也是按照每一個animate的劃分封裝了各自動畫組的行為,

包括數據過濾、緩動公式、一些動畫默認參數的設置、元素狀態的調整、事件的處理通知機制、執行等等

換句話說,我們可以把animate看作一個對象,對象封裝自己的一系列屬性與方法。

jQuery可以支持連續動畫,那么animate與animate之間的切換就是通過隊列.queue,這個之前就已經詳細的解釋過了

 

動畫的參數

jQuery的內部的方法都是針對API的處理范圍設計的

我們看看Animation方法的支持情況:

.animate( properties [, duration ] [, easing ] [, complete ] ) .animate( properties, options )

  • 區別就與第二組數據的傳遞了,options是支持對象傳參
  • properties參數就是寫一個CSS屬性和值的對象,動畫都是涉及變化的,那么什么值才能變化?
  • 理論上來說有數值的屬性都是可以變化的,width, height或者left可以執行動畫,但是background-color不能,但是也不是絕對的,主要看數據的解析度,可以用插件支持
  • 除了樣式屬性, 一些非樣式的屬性,如scrollTopscrollLeft,以及自定義屬性,也可應用于動畫
  • 除了定義數值,每個屬性能使用'show', 'hide', 和 'toggle'。這些快捷方式允許定制隱藏和顯示動畫用來控制元素的顯示或隱藏。為了使用jQuery內置的切換狀態跟蹤,'toggle'關鍵字必須在動畫開始前給定屬性值

簡單的來說,就是把一對的參數丟大animate方法里面,然后animate就開始執行你參數規定的動畫了,

那么動畫每執一次就會通過回調通知告訴開發者,具體有complete/done/fail/always/step接口等等

 

理解定義

<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元素的起始

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

jQuery源碼分析系列(40): 動畫設計

看到沒有,這個值其實很符合定時器的特性,也是一個沒有規律的值

根據這個值,我們可以得出當前位置的一個百分比了

var remaining = Math.max(0, startTime + options.duration - createTime()) var temp = remaining / options.duration || 0; var percent = 1 - temp;

pecent

jQuery源碼分析系列(40): 動畫設計

那么這個移動的距離就很簡單了

我把整個公式就直接列出來了

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的事件反饋
  • 增加屬性的show/hide/toggle的快捷方式
  • 可以給css屬性設置獨立的緩動函數

 

基于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源碼分析系列(40): 動畫設計

通過這個結構大概就知道了,這個就是用于生成動畫算法所需要的一些數據與算法的具體流程控制了

 

屬性預處理

  • 針對height/width動畫的時候,要先處理本身元素溢出
  • 針對height/width動畫的時候,元素本身的inline狀態處理

我們知道元素本身在布局的時候可以用很多屬性對其設置,可是一旦進行動畫化的話,某些屬性的設置可能會對動畫的執行產生副作用,所以針對這樣的屬性,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動畫其實原理上本身是不復雜的。量變產生質變,通過擴展大量的便捷方式加大了邏輯上的難度,但是從根本上來說:

主要包括:

  • 屬性過濾specialEasing處理的propFilter方法
  • 通過Deferred生成流程控制體系
  • 通過defaultPrefilter方法對動畫執行的臨時修正
  • 通過createTween方法,生成動畫的算法與流程控制器
  • 最后通過setInterval來控制每一個createTween對象的執行

大體上jQuery的動畫就這么些內容,當然還有一些細節的話 遇到在提出來了,下章就會通過上面的這些處理,實現一個類jquery動畫的模擬了,加強理解

您可能感興趣的文章:
jquery動畫1.加載指示器
jQuery中實現動畫效果的基本操作介紹
jquery動畫1.加載指示器
用js實現的模擬jquery的animate自定義動畫(2.5K)
從零開始學習jQuery (七) jQuery動畫實現 讓頁面動起來
六款幫助你實現驚艷視差滾動效果的jQuery插件
2012年開發人員的16款新鮮的jquery插件體驗分享
jQuery 數據緩存模塊進化史詳細介紹
jQuery源碼分析-01總體架構分析
jQuery UI 教程之一 ——入門

上一篇:jQuery插件教程 下一篇:jQuery動畫的實現
[關閉]
微信下载APP完成任务如何赚钱 甘肃新11选5遗漏号码 二四六免费资料大全正版 云南11选5杀号 中国福利彩票重庆欢乐生肖 海南环岛赛游戏 彩票开奖黑龙江6十1 786百家乐怎么赌钱 短期投资理财平台推荐 河北福彩排列7开奖号 江西时时彩 走势