根據上圖實現除doAnimation
外的邏輯:html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery之$().animate()的實現</title> </head> <body> <script src="jQuery.js"></script> <div id="A" style="width:100px;height:50px;background-color: deeppink">這是A</div> <script> //匿名函數自調用,下面好長好長的function就是$ //也就是說$是一個function(){xxx} (function($) { window.$ = $; })( //這裏也是匿名函數自調用 //本質就是通過一系列操做獲得chenQuery並做爲參數$,賦值給window.$ function() { //匹配ID let rquickExpr = /^(?:#([\w-]*))$/; //jQuery初始化 function chenQuery(selector) { return new chenQuery.fn.init(selector); } //假設是在數據緩存中存取隊列 const Queue=[] //數據緩存 const dataPriv={ get:function (type) { if(type==='queue') return Queue }, } const dequeue=function() { const Queue=dataPriv.get("queue") let fn = Queue.shift() //當單個動畫結束後,執行下個動畫 const next = function() { dequeue(); } if ( fn === "inprogress" ) { fn = Queue.shift(); } if (fn) { Queue.unshift( "inprogress" ); /*執行doAnimation方法,doAnimation(element, options,function() {firing = false;_fire();})*/ /*fn的參數就是形參func*/ /*func方法是用來通知上個動畫結束,下個動畫運行的重要function*/ //func的做用是用來通知動畫執行結束,並繼續執行下一個動畫 const func=function() { next(); } fn(func); } } //省略type const queue=function(element, options, callback, ) { //模仿從數據緩存中獲得的隊列,直接寫Queue.push也行 const Queue=dataPriv.get("queue") //向動畫隊列中添加doAnimation觸發器 Queue.push(function(func) { //doAnimation callback(element, options, func); }); //若是沒有動畫在運行,運行動畫 //動畫鎖inprogress if(Queue[0]!=='inprogress'){ dequeue() } } /*動畫*/ const animation = function(element,options) { const doAnimation = function(element, options, func) { const width = options.width /*===這裏面定義了動畫的算法,也就是Animation實現的地方===*/ // 默認動畫時長2s element.style.transitionDuration = '400ms'; element.style.width = width + 'px'; /*監聽單個動畫完結*/ //transitionend 事件在 CSS 完成過渡後觸發 element.addEventListener('transitionend', function() { func() }); } //每調用一次animation,就入一次隊 return queue(element, options,doAnimation,); } //爲chenQuery的fn和prototype原型屬性 賦 animate屬性 chenQuery.fn = chenQuery.prototype = { //也能夠直接把animation裏的代碼放這裏來,但這樣就太長了,下降了可讀性 animate: function(options) { animation(this.element, options); //注意返回的是this,也就是$("#A"),這樣就能繼續調用animate方法 // 也就是鏈式調用 return this; } } //爲chenQuery的fn屬性添加init方法 const init = chenQuery.fn.init = function(selector) { // ["#A", "A",groups: undefined,index: 0,input: "#A"] const match = rquickExpr.exec(selector); //這邊默認是隻找id的元素 const element = document.getElementById(match[1]) //this指chenQuery.fn.init方法 //爲該方法添加element屬性 this.element = element; //返回chenQuery.fn.init return this; } //挺繞的,再將init的原型等於chenQuery.fn方法 init.prototype = chenQuery.fn; //chenQuery自己是一個function(){} // chenQuery{ //init能調用fn,fn能調用init // fn:{ // animate:function(){}, // init:function(){}, // // init.prototype=fn // }, // prototype:{ // animate:function(){}, // } // } return chenQuery; }()); const A = document.querySelector('#A'); //在異步調用中,進行同步調用 //動畫是異步的 A.onclick = function() { //就是連續調用animation.add() $('#A').animate({ 'width': '500' }).animate({ 'width': '300' }).animate({ 'width': '1000' }); }; </script> </body> </html>
解析:
(1)匿名函數自調用的參數:算法
(function(a){ console.log(a) //name })('name') (function (b) { console.log(b) //function(){console.log('name')} })(function () { console.log('name') })
(2)快速匹配id選擇器緩存
//匹配ID let rquickExpr = /^(?:#([\w-]*))$/;
(3)inprogress是動畫鎖
當第一個動畫執行時,向Queue
中添加鎖inprogress
,阻止異步調用動畫,也就是要求同步執行動畫,當動畫結束時,移除鎖,調用下一個動畫。異步
(4)transitionendtransitionend
事件在 CSS 完成過渡後觸發,這裏當作單個動畫完成的信號,觸發後,會告知下個動畫進行函數
下圖的實現將在下篇文章貼出:動畫
(完)ui