suis 2019-11-16
根据上图实现除doAnimation
外的逻辑:
<!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 完成过渡后触发,这里当做单个动画完成的信号,触发后,会告知下个动画进行
下图的实现将在下篇文章贴出:
(完)