共计 3105 个字符,预计需要花费 8 分钟才能阅读完成。
根据上图实现除 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 完成过渡后触发,这里当做单个动画完成的信号,触发后,会告知下个动画进行
下图的实现将在 下篇 文章贴出:
(完)
正文完
发表至: javascript
2019-06-29