根据上图实现除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)匿名函数自调用的参数:
代码语言:javascript复制 (function(a){
console.log(a) //name
})('name')
(function (b) {
console.log(b) //function(){console.log('name')}
})(function () {
console.log('name')
})
(2)快速匹配id选择器
代码语言:javascript复制 //匹配ID
let rquickExpr = /^(?:#([w-]*))$/;
(3)inprogress是动画锁
当第一个动画执行时,向Queue
中添加锁inprogress
,阻止异步调用动画,也就是要求同步执行动画,当动画结束时,移除锁,调用下一个动画。
(4)transitionend
transitionend
事件在 CSS 完成过渡后触发,这里当做单个动画完成的信号,触发后,会告知下个动画进行
下图的实现将在下篇文章贴出: