• 开篇一张图之队列模型
  • queue()如何使用?
  • queue()原理实现?
  • 基于queue()模拟实现animate()

一、使用queuer方法、理解队列原理

  • queue()
  • dequeue()
  • clearQueue()

1.创建队列$(selector).queue(queueName,function);

//html --css省略
<div class="demo"></div> //js -- 创建队列chain,并传入3个方法
$(".demo").queue("chain",function(){
console.log("over1");
}).queue("chain",function(){
console.log("over2");
}).queue("chain",function(){
console.log("over3");
});

2.查看队列:

console.log( $(".demo").queue("chain") );
//打印结果:[function, function, function]

3.出队:$(selector).dequeue(queueName)

$(".demo").dequeue("chain");//打印:over1
console.log( $(".demo").queue("chain") );//打印:[function, function]
$(".demo").dequeue("chain");//打印:over2
console.log( $(".demo").queue("chain") );//打印:[function]

在第一次出队操作后,查看控制台打印的队列数组索引任然是从0开始,说明队列出队操作是执行最先添加的函数,并在执行后删除这个函数。而且依然保持数组索引从零开始。

4.从前面的出队机制来看有点鸡肋,每次都是触发一个函数,删除一个函数,如果有需求是要一次完全触发执行呢?至少在animate的动画实现来看就是一次触发全部执行。所以jQuery提供了这样的机制,就是我们在创建队列传入函数的时候,给函数传入参数next,并在函数内最末尾处添加代码next();这样就可以实现触发一次可以执行最开始的函数,并且可以接着执行下一个函数,而且这两个函数都会被出队。

$(".demo").queue("chain",function(next){
console.log("over1");
next();
}).queue("chain",function(next){
console.log("over2");
next();
}).queue("chain",function(){
console.log("over3");
});
$(".demo").dequeue("chain");//打印:over1 over2 over3
console.log( $(".demo").queue("chain") );//打印:[]

5.清空队列$(selector).clearQueue(queueName):clearQueue() 方法从尚未运行的队列中移除所有项目。

$(".demo").clearQueue("chain");
console.log( $(".demo").queue("chain") );//[]

二、jQuery之animate中的queue(队列)(实现原理)

队列作为一种数据结构其底层实质就是数据,因为数组本身就是有序列的数据结构,只是队列在数组的基础上规定了添加、调用、删除三个固定的行为,为什么叫做固定的行为呢?就是因为给队列添加数据单元的时候只能添加在末尾;而队列执行就是调用加删除,而且只能从开始处调用一个,调用同时从队列中删除该数据单元。所以,这里需要用到数据的两个原生操作方法push()和shift():在数组末尾添加和删除数组第一个元素并返回该元素。下面是在仿写jQuery的jQuery对象下封装的queue方法源码:

//队列(入队) -- 添加队列 - 往已有的队列添加内容
jQuery.prototype.myQueue = function(){
var queueName = arguments[0] || 'fx';
var addFunc = arguments[1] || null;
var len = arguments.length;
//获取队列
if(len == 1){
return this[0].queueObj[queueName];
}
//queue Data dom {chain : []} -- 添加队列 || 往已有队列中添加内容
for(var i = 0; i < this.length; i ++){
if(this[i].queueObj == undefined){
this[i].queueObj = {}
}
this[i].queueObj[queueName] == undefined ? this[i].queueObj[queueName] = [addFunc] : this[i].queueObj[queueName].push(addFunc);
}
return this;
}

在jQuery源码中,队列数据是通过jQuery的data机制存储了,由于data机制比较复杂,这里就在DOM原型上添加了一个queueObj属性来存储队列数据。jQuery源码中queue方法实现了获取队列和相队列添加数据的功能,所以这两个功能也一并在myQueue方法中实现了,并且也可以实现DOM集合的jQuery对象的逐个操作,这个和元素方法使用已经没有差别,只有数据存储的差异了。

接着我们再来看看dequeue出队的方法实现源码:

////队列 -- 出队
jQuery.prototype.myDequeue = function(type){
var queueName = arguments[0] || 'fx';
var queueArr = [];
var currFunc = null;
var next = null;
for(var i = 0; i < this.length; i ++){
var self = this[i];
queueArr = jQuery(self).myQueue(queueName);
currFunc = queueArr.shift();
if(currFunc == undefined){
break;
}
next = function(){
jQuery(self).myDequeue(queueName);
}
currFunc(next);
}
return this;
}

出队时需要注意一点就是给animate()动画方法设置默认队列名称“fx”,同时也具备全部jQuery对象的所有DOM执行出队操作。

一、基于queuer方法实现原生jQuery动画函数animate()

其实大部分的代码已经在定时点的运动中实现了,而且从实现功能来看定时定点运动函数已经可以通过回调函数来实现了,但是在jQuery源码中,animate()方法是通过队列的方式来取代回调模式,这是为了更好的面向实际开发需要,队列模式相比回调模式更容易维护,出现异常更容易排除,后期会有关于回调的探讨博客,会对这个问题做具体的讨论。

当然是用队列的方式来实现animate()还有更关键的原因,就是可以控制动画延迟,停止,取消动画效果,具体可以了解jQuery使用(八):运动方法。这里我们今天暂时实现animate()方法和动画延迟方法delay(),要实现停止和取消动画效果还需要其他功能协助才能完成,那是后面的事了。下面是animate()实现源码:

 //动画函数 -- 模拟实现animate -- 暂时实现基于目标点和回调函数两个参数的动画
//参数:{styles},speed,easing,callback
jQuery.prototype.myAnimate = function(json,speed,easing,callback){
var len = this.length;
var self = this;
//最后添加到队列里的内容函数
var baseFunc = function(next){
var times = 0; //记录到达目标点的DOM个数,用于判断是否是否DOM都到达目标点
for(var i = 0; i < len; i++){
startMove(self[i],json,speed,easing,function(){
times++;
if(times == len){ //如果所有DOM动画执行完毕,调用回调函数执行
callback && callback();
next(); //所有DOM执行完动画后,并且回调函数执行完,执行动画队列的下一个动画
}
});
}
}
this.myQueue('fx',baseFunc);
if(this.myQueue('fx').length == 1){
this.myDequeue('fx');
}
//获取dom样式
function getStyle(obj,attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj,false)[attr];
}else{
return obj.currentStyle[attr];
}
} //运动方法 -- 具体参照博客《原生JavaScript运动功能系列(五):定时定点运动》
function startMove(obj,json,speed,easing,callback){
var initialPlace = {};
var nowPlace;
clearInterval(obj.timer);
var createTime = function(){
return (+new Date);
}
var startTime = createTime();
for(var attr in json){
if(attr == 'opacity'){
initialPlace[attr] = Math.round(parseFloat(getStyle(obj,attr))*100);
}else{
initialPlace[attr] = parseInt(getStyle(obj,attr));
}
}
if(!easing){
easing = jQuery.easingObj.swing;
}else{
easing = jQuery.easingObj[easing];
}
obj.timer = setInterval(function(){
var remaining = Math.max(0, startTime + speed - createTime());
var temp = remaining / speed || 0;
var percent = 1 - temp;
for(var attr in json){
nowPlace = (json[attr] - initialPlace[attr]) * easing(percent) + initialPlace[attr];
if(attr == 'opacity'){
obj.style.opacity = nowPlace / 100;
}else{
obj.style[attr] = nowPlace + 'px';
}
}
if(percent == 1){
clearInterval(obj.timer);
typeof callback == 'function' ? callback() : '';
}
},30);
}
return this;
}

由于这部分代码量比较大,整体方法的代码我先折叠,但是别急,我把关键的代码提炼出来分析:

 var len = this.length;
var self = this;
//最后添加到队列里的内容函数
var baseFunc = function(next){
var times = 0; //记录到达目标点的DOM个数,用于判断是否是否DOM都到达目标点
for(var i = 0; i < len; i++){
startMove(self[i],json,speed,easing,function(){
times++;
if(times == len){ //如果所有DOM动画执行完毕,调用回调函数执行
callback && callback();
next(); //所有DOM执行完动画后,并且回调函数执行完,执行动画队列的下一个动画
}
});
}
}
this.myQueue('fx',baseFunc);
if(this.myQueue('fx').length == 1){
this.myDequeue('fx');
}

其实animate()方法非常的简单,本质上就是对队列的应用,先将动画需要的参数和动画函数合并到一个方法内(baseFunc),并且这个方法带有形参next。然后将这个方法添加到每个DOM的动画队列(“fx”)中(16行)。接着18行就将这个刚刚添加到队列中的函数进行出队操作,animate方法末尾有放回this,所以又会接着操作一下个animate方法,链式调用时jQuery的基本特性,我在仿写的jQuery中也有实现。

需要注意的是运动函数有个地方需要改一下(相对定点定时运动):

if(!easing){
easing = jQuery.easingObj.swing;
}else{
easing = jQuery.easingObj[easing];
}

因为运动函数调用时this指向是window,如果将easingObj放到animate方法内部的话没办法获取,所以在jQuery对象上定一个属性为easingObj,然后在运动函数中通过jQuery对象来调用,在jQuery源码中也是这么操作的。接着下面是动画延迟方法myDelay()的源码:

 //动画延迟
jQuery.prototype.myDelay = function(duration){
var len = this.length;
var queueArr = this[len-1].queueObj['fx'];
queueArr.push(function(next){
setTimeout(function(){
next();
},duration);
});
return this;
}

这个实现起来也是非常的简单,本质上就是在方法内设置一个定时器,定时器的回调函数内写入next执行,然后将这个方法添加到fx队列中,当定时器延迟时间一到就执行下一个动画函数。

最关键的原理:定时器的异步成就了animate()方法的链式调用,不然你想想如果没有异步,后面的动画是怎么被添加进去的,这里的异步应用才是animate的法宝,当第一个animate被执行添加第一个动画后就马上被进行出列操作,那后面的动画是怎么被放到队列中的呢?因为当第一次出队时动画进入了异步程序,所以并没有阻塞在第一个animate,而是动画进入异步执行,在动画异步倒计时的时候,animate动画函数的链式调用早就快速的执行完毕了,所以当第一个动画执行完以后,可以直接使用next()方法做出队操作。

//用下面这部分代码来理解这个最核心的原理(用来理解异步程序)
$(".demo")[0].obj = [function(){console.log(1)}];
$(".demo")[0].aa = function(){
var a = this;
var sum = 0;
var time = setInterval(function(){
a.obj[sum]();
sum++;
if( sum == 2 ){
clearInterval(time);
}
},3000);
return this;
}
$(".demo")[0].aa().obj.push(function(){console.log(2)});

jQuery使用(九):队列及实现原理、基于队列模拟实现animate()的更多相关文章

  1. Atitit事件代理机制原理 基于css class的事件代理

    Atitit事件代理机制原理 基于css class的事件代理 1.1. 在javasript中delegate这个词经常出现,看字面的意思,代理.委托1 1.2. 事件代理1 1.3. 代理标准化规 ...

  2. atitit. java queue 队列体系and自定义基于数据库的队列总结o7t

    atitit. java queue 队列体系and自定义基于数据库的队列总结o7t 1. 阻塞队列和非阻塞队列 1 2. java.util.Queue接口, 1 3. ConcurrentLink ...

  3. 高性能消息队列 CKafka 核心原理介绍(上)

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:闫燕飞 1.背景 Ckafka是基础架构部开发的高性能.高可用消息中间件,其主要用于消息传输.网站活动追踪.运营监控.日志聚合.流式 ...

  4. 消息队列mq的原理及实现方法

    消息队列技术是分布式应用间交换信息的一种技术.消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走.通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置.或在继续执行前不需要等待 ...

  5. jQuery的内部运行机制和原理

    jQuery的优点: jQuery是一个非常优秀的JavaScript库,与Prototype,YUI,Mootools等众多的Js类库相比,它剑走偏锋,从Web开发实用的角度出发,抛除了其它Lib中 ...

  6. Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理

    Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...

  7. JUC之Java中的阻塞队列及其实现原理

    在文章线程池实现原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线程池的组成部分,其中一个组成部分就是阻塞队列.那么JAVA中的阻塞队列如何实现的呢? 阻塞队列,关键字是阻塞 ...

  8. u-boot移植总结(三)(转)S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A)

    S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A) 转载自:http://www.cnblogs.com/idle_man/archive/2010/12/23/19153 ...

  9. jQuery插件实现的方法和原理简单说明

    下文来自 http://www.itzhai.com/jquery-plug-ins-to-achieve-the-methods-and-principles-of-simple-instructi ...

随机推荐

  1. MFC界面相关源码

    这是这4篇MFC界面的相关源码.建议学习Visual C++的看看这2本微软官方出的教材. [MFC Windows程序设计(第2版,修订版)](美)Jeff Prosise著 [Windows程序设 ...

  2. 如何制作中文Javadoc包,并导入到Eclipse

    原理:使用chm转换工具将chm文件转换为zip文件,导入eclipse中即可. 准备 JDK1.9 API 中文 谷歌翻译版:http://www.pc6.com/softview/SoftView ...

  3. LeetCode算法题-Search in a Binary Search Tree(Java实现)

    这是悦乐书的第295次更新,第314篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第163题(顺位题号是700).给定一个二叉搜索树(BST)的和正整数val. 你需要在 ...

  4. 7 Best Free RAR Password Unlocker Software For Windows

    Here is the list of Best Free RAR Password Unlocker Software for Windows. These software run differe ...

  5. 【Teradata】扩容操作步骤

    第一章,前期准备(旧系统信息收集) 1.DBScontrol关键信息 DBSCONTROL系统参数是在节点上设置的,其参数直接关系到系统全局,需要慎重设置,新节点的关键参数要与生产库一致或者相容.主要 ...

  6. .net core2.1 三层中使用Autofac代替原来Ioc

    首先,现有的三层项目的结构 其中  Repository public interface IPersonRepository { string Eat(); } public class Perso ...

  7. Oracle通过Navicat导入表数据与机构,数据无法直接查询,需要加双引号的问题

    使用navicat 导入表到ORACLE时,总是会遇到虽然表格完整导入到数据库,但是往往查不出来数据,网上提供的解决办法是把查询的列 加上 双引号,或者表名加上双引号,但这解决办法却减慢了编写sql ...

  8. 关于表单元素的name及HTML中的id

    这种在上高级WEB课时,老师为表单元素赋了name值,之后直接在JS中使用该值而不需要使用document.get...来获取了,例: <!DOCTYPE html> <html&g ...

  9. 小程序——阿里服务器配置https及什么是IIS

    1.申请域名:阿里云 2.免费开启SSL证书:管理=>免费开启SSL证书>单域名>dev.xxx.top 3.配置服务器:下载=>IIS7证书 *注册一个域名,可以免费开启一个 ...

  10. python操作MONGODB数据库,提取部分数据再存储

    目标:从一个数据库中提取几个集合中的部分数据,组合起来一共一万条.几个集合,不足一千条数据的集合就全部提取,够一千条的就用一万减去不足一千的,再除以大于一千的集合个数,得到的值即为所需提取文档的个数. ...