• 开篇一张图之队列模型
  • 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. sizeof和strlen()区别及用法

    //sizeof是以字节为单位计算变量或类型所占内存大小,它是属于C语言运算符系列:而strlen()是一个函数,是计算字符串长度(也是以字节为单位,但略有区别):比如: char array[] = ...

  2. 英语口语练习系列-C07-谈女孩

    <将进酒>·李白 君不见黄河之水天上来,奔流到海不复回. 君不见高堂明镜悲白发,朝如青丝暮成雪. 人生得意须尽欢,莫使金樽空对月. 天生我材必有用,千金散尽还复来. 烹羊宰牛且为乐,会须一 ...

  3. Spring类型转换(Converter)

    Spring的类型转换 以前在面试中就有被问到关于spring数据绑定方面的问题,当时对它一直只是朦朦胧胧的概念,最近稍微闲下来有时间看了一下其中数据转换相关的内容,把相应的内容做个记录. 下面先说明 ...

  4. html+css 制作简易导航栏

    二话不说直接上代码(萌新:实在也没什么好说的) <!DOCTYPE html> <html lang="en" xmlns="http://www.w3 ...

  5. There Are Now 3 Apache Spark APIs. Here’s How to Choose the Right One

    See Apache Spark 2.0 API Improvements: RDD, DataFrame, DataSet and SQL here. Apache Spark is evolvin ...

  6. linux-python3.8安装

    环境:  centos7.5 版本:python3.8 1.依赖包安装 yum -y install zlib-devel bzip2-devel openssl-devel ncurses-deve ...

  7. 【Consul】CONSUL调研

    [Consul]CONSUL调研 2016年08月18日 18:31:53 YoungerChina 阅读数:1962更多 所属专栏: Consul修炼   版权声明:原创不易,转载请注明出处! ht ...

  8. Svn 安装、配置、使用指南

    Svn 安装.配置.使用指南 Svn 是 Subversion 的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统. 1. 安装配置 1.1. 安装 svn 1.2. 创建 svn 仓库 1 ...

  9. 并发编程-JUC之Atomic

    概述: 早期的JDK版本中,如果要并发的对Integer.Long.Double之类的Java原始类型或引用类型进行操作,一般都需要通过锁来控制并发,以防止数据不一致.JUC-Atomic原子类位于j ...

  10. Zookeeper连接eclipse

    package com.bw.ZK; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.ap ...