根据Bootstrap中文网的介绍,Unslider一个超小的 jQuery轮播(slider)插件,参照这个汉化版的介绍页面,这个插件有你需要的优点,但是本文是抱着学习的态度,学习如何实现轮播插件,所以有些细节可能有所忽略。
1. 如何使用
参照Bootstrap中文网提供的介绍页面,或者参照官网的介绍都是可以,虽然unslider已经升级了版本,但是使用方式(API接口)还是没有改变。
对于HTML结构的要求只需要提供类似以下结构即可:
然后引入jquery.js和unslider.js两个文件,即可以在DOM加载完执行
$(function() {
$('.banner').unslider();
});
我取汉化版介绍页面的元素,使用最新版的unslider.js,调用unslider(),比较页面元素有什么变化。
源代码
使用插件后的效果(有所节省)
可以发现使用插件后,会在.banner上封装
.banner设置样式不让子元素溢出;在ul上设置宽度是li元素的整数倍,li元素的所有兄弟元素平均结果(100/4);还加上next和prev元素,加上了nav导航。
ul是相对于.banner定位的,虽然宽度是大于100%,但是.banner是不会被ul撑开的;而在ul上配置width和left参数,可以控制显示ul的起始位置,left:-100%相当于ul向左飘过去了100%,通俗点说:
父元素
.banner只能让ul显示一个身位,但是ul膨胀了,实际它有4个身位,相对于.banner定位,默认left:0%时,
相当于显示0-1身位的ul,为了显示第二个身位的ul,就必须将ul往左移,让它显示1-2位置的ul的,所以此时设置left: -100%,
以此类推。
2. 使用 $.fn.unslider 方法
$.fn.unslider方法是在jQuery原型链定义的方法,jQuery对象自然能够调用这个方法。前面的例子中我们是直接调用的,并没有传入参数,事实上$.fn.unslider还可以接收类似这样的参数:$(".banner").unslider("fn:arg1,arg2")。最终调用在某个位置定义的fn函数,参数是arg1和arg2。
2.1 分析 $.fn.unslider 源码
// And set up our jQuery plugin
$.fn.unslider = function(opts) {
return this.each(function() {
var $this = $(this);
// Allow usage of .unslider('function_name')
// as well as using .data('unslider') to access the
// main Unslider object
if(typeof opts === 'string' && $this.data('unslider')) {
opts = opts.split(':');
var call = $this.data('unslider')[opts[0]];
// Do we have arguments to pass to the string-function?
if($.isFunction(call)) {
return call.apply($this, opts[1] ? opts[1].split(',') : null);
}
}
return $this.data('unslider', new $.Unslider($this, opts));
});
};
$.fn.unslider的重要逻辑都是在$.Unslider中实现的,第一次调用$.fn.unslider方法时将调用jQuery.data方法将新构造的$.Unslider实例保存到jQuery对象的缓存对象上,供后续使用;后续的调用可以直接从这个jQuery缓存对象取出$.Unslider实例调用相关方法。这样做的好处就是不会多执行$.Unslider构造方法?(好像是我自己编出来的一个理由)
jQuery插件一般最终都会在jQuery原型上定义要被jQuery对象调用的方法,或者通过直接定义的方式,如$.fn.myPlugin = function(){},或者首先定义好插件方法,然后通过$.fn.extend扩展方法将插件方法扩展到jQuery原型上。unslider插件通过了在jQuery定义静态方法$.Unslider,而$.fn.unslider只是调用入口,所有的业务逻辑都能通过$.Unslider来完成。
3. $.Unslider
首先可以把$.Unslider(context, options)看作构造函数,最终会被$.fn.unslider(options)调用。context参数是一个jQuery对象,对应要生成轮播效果的$('.banner')集合的某个元素的jQuery对象,即$($('.banner')[0]); options最终会被扩展到$.Unslider的默认参数中。
首先看$.Unslider内部对this的处理,内部会对this备份到self变量,后续的属性和方法都在self基础上定义。
$.Unslider = function(context, options) {
var self = this;
// Create an Unslider reference we can use everywhere
self._ = 'unslider';
...
}
我的理解,new $.Unslider的调用方法,在$.Unslider内部的this是指向$.Unslider对象自己的,如果是$('#id').Unslider()就不一样了,此时this会指向#idDOM元素,当然目前$.Unslider静态方法是无法被jQuery对象直接调用的。
3.1 $.Unslider 整体结构
整体结构:
$.Unslider = function(context, options) {
var self = this;
//插件标识
self._ = 'unslider';
//默认参数
self.defaults = {
};
/**
* 参照生成后的页面元素做个类比
* self.$parent =>
* self.$context =>
* self.$container =>
* self.slides =>
*/
//备份jQuery对象
self.$context = context;
self.options = {};
//容器的父层
self.$parent = null;
//轮播的容器jQuery,最终是self.$context的子元素的jQuery对象
//$('.banner>ul')
self.$container = null;
//每个轮播的页面
selft.$slides = null;
//导航组
self.$nav = null;
//左右指示
self.$arrows = [];
//轮播页面总数
self.total = 0;
//当前轮播页面的序号
self.current = 0;
//前缀
self.prefix = self._ + '-';
//用于监听事件的后缀,是监听事件的命名空间
self.eventSuffix = '.' + self.prefix + ~~(Math.random() * 2e3);
//定时器
self.interval = null;
//初始化方法
self.init = function() {
self.options = $.extend({}, self.defaults, options);
//self.$container
//self.$slides
self.setup();
$.each(['nav', 'arrows', 'keys', 'infinite'], function(index, module) {
self.options[module] && && self['init' + $._ucfirst(module)]();
});
//self.initSwipe();
self.options.autoplay && self.start();
self.calculateSlides();
self.$context.trigger(self._ + '.ready');
return self.animate(self.options.index || self.current, 'init');
}; //end of self.init
self.setup = function() {
//css
};
self.calculateSlides = function() {
self.total = self.$slides.length
//set total height or width
};
self.start = function() {
self.interval = setTimeout(function() {
self.next();
}, self.options.delay);
return self;
};
self.stop = function() {
clearTimeout(self.interval);
return self;
};
self.initNav = function() {
};
self.initArrows = function() {
};
self.initKeys = function() {
};
self.initSwipe = function() {
};
self.destroyArrows = function() {};
self.destroySwipe = function() {};
self.destroyKeys = function() {};
self.setIndex = function(to) {
};
self.animate = function(to, dir) {
};
self.next = function() {
};
self.prev = function() {
};
self.animateHorizontal = function(to) { };
self.animateVertical = function(to) { };
self.slide = function(prop, to) {
};
self.animateFade = function() {};
self._move = function($el, obj, callback, speed) {} ;
//最终调用init方法,返回self,见self.init定义
return self.init(options);
};
除$.Unslider这个静态方法外,unslider插件还在jQuery原型上定义辅助方法:
$.fn._active = function(className) {
};
$._ucfirst = function(str) {
};
$.fn._move = function() {
};
整体结构非常类似面向对象的做法,如果$.Unslider是一个类定义,而$.Unslider(context, options)就是构造函数,其他self.开头的属性和方法就是这个类的成员变量和成员方法。
其实以_开头的方法可以理解成私有方法,unslider并不想把它暴露出去。事实上,$.Unslider的所有定义的方法都能够被外部调用,除非使用闭包的方式。
var Unslider = (function() {
function init(context, options) {} //初始化方法
function _move() {}
function next() {
//内部调用_move,但是整体没有暴露_move方法
}
var defaults = {
};
return {
init: init
next: next
};
})();
$.fn.unslider = {};
$.fn.extend($.fn.unslider, Unslider);
使用方式上可能就有点不同了。
3.2 $.Unslider 源码分析
//开始重要的源码分析
3.2.1 默认参数
$.Unslider = function(context, options) {
var self = this;
// Create an Unslider reference we can use everywhere
self._ = 'unslider';
// 默认参数会被扩展到self.options
// 最终会被外部传入的options参数覆盖,见self.init方法
self.defaults = {
// 是否自动开始
autoplay: false,
// 动画间隔微秒
delay: 3000,
// 速度微秒
speed: 750,
// An easing string to use. If you're using Velocity, use a
// Velocity string otherwise you can use jQuery/jQ UI options.
easing: 'swing', // [.42, 0, .58, 1],
// 键盘事件相关
keys: {
prev: 37,
next: 39
},
// 是否需要设置导航,设置为true在self.init方法中会调用initNav方法
nav: true,
// 上一个和下一个的指示元素
// 默认参数扩展到self.options后
// self.options["arrows"]可以转换为true,在self.init方法中会调用initArrows方法
arrows: {
prev: 'Prev',
next: 'Next'
},
// 方向
animation: 'horizontal',
// 选择器表达式
selectors: {
container: 'ul:first',
slides: 'li'
},
// Do you want to animate the heights of each slide as
// it moves
animateHeight: false,
// Active class for the nav
activeClass: self._ + '-active',
// Have swipe support?
// You can set this here with a boolean and always use
// initSwipe/destroySwipe later on.
swipe: true
};
...
};
3.2.2 init方法
初始化方法init是由构造方法在内部调用的,最终返回这个对象self。
// Get everything set up innit
self.init = function(options) {
// 扩展合并外部传入的参数和默认参数
// 这种写法不会破坏原来的self.defaults,扩展的结果都放在{}
self.options = $.extend({}, self.defaults, options);
// 对容器进行封装,添加样式目的是让容器相对与父元素相对定位
// 参照`unslider-wrap`这个类样式
self.$container = self.$context.find(self.options.selectors.container).addClass(self.prefix + 'wrap');
// 备份保存所有的轮播页面jQuery对象
self.$slides = self.$container.children(self.options.selectors.slides);
// 调用setup方法
self.setup();
// self.options合并后的选项
// 如果存在相应的参数,且能转换为true,则调用相应的初始化方法
$.each(['nav', 'arrows', 'keys', 'infinite'], function(index, module) {
// $._ucfirst利用正则表达式将首字母转换为大写
self.options[module] && self['init' + $._ucfirst(module)]();
});
// 如果引入了jquery.event.move.js和jquery.event.swipe.js文件就执行
// 和动画相关的另外一个实现方法,与jQuery.animate同等的velocity
if(jQuery.event.special.swipe && self.options.swipe) {
self.initSwipe();
}
// 是否自动开始
self.options.autoplay && self.start();
// 计算
self.calculateSlides();
// 触发自定义的事件
self.$context.trigger(self._ + '.ready');
// 开始运动到指定序号的页面
return self.animate(self.options.index || self.current, 'init');
};
本文中没有打算引入velocity,轮播效果最终由jQuery.animate来完成,这应该不阻碍对整个unslider插件代码的梳理分析。
init只是初始化过程中的一个入口,它还需要其他初始化方法来帮助完成其他业务逻辑,包括setup、initNav、initArrows、initKeys、initInfinite、calculateSlides等方法。接下来会逐个分析它们。
3.2.3 setup方法
self.setup = function() { //给轮播容器的复层(.banner)做封装 self.$context.addClass(self.prefix + self.options.animation).wrap(''); //备份容器的父层,即刚才的封装层 self.$parent = self.$context.parent('.' + self._); // We need to manually check if the container is absolutely // or relatively positioned var position = self.$context.css('position'); // If we don't already have a position set, we'll // automatically set it ourselves if(position === 'static') { self.$context.css('position', 'relative'); } self.$context.css('overflow', 'hidden'); };
setup方法主要目的是对.banner($context)做封装,设置$context的样式,如果事先没有$context的position,就设置它相对定位position:relative,设置overflow:hidden,这样只显示ul的一部分。
3.2.4 initNav方法
self.initNav = function() {
// HTML5到导航标签
var $nav = $('
');
// 遍历轮播页面对象
self.$slides.each(function(key) {
// 从元素的属性或者序号中获取
var label = this.getAttribute('data-nav') || key + 1;
// 是否执行回调函数,这块不是很明白
if($.isFunction(self.options.nav)) {
label = self.options.nav.call(self.$slides.eq(key), key, label);
}
// 增加导航项
$nav.children('ol').append('' + label + ' ');
});
// 插入到$context并保存起来
self.$nav = $nav.insertAfter(self.$context);
// 绑定监听事件 self.eventSuffix是命名空间,实际监听事件还是`click`
self.$nav.find('li').on('click' + self.eventSuffix, function() {
// Cache our link and set it to be active
var $me = $(this).addClass(self.options.activeClass);
// Set the right active class, remove any other ones
$me.siblings().removeClass(self.options.activeClass);
// 轮播到某个页面 参数是序号
self.animate($me.attr('data-slide'));
});
};
导航的这些DOM元素是在js代码中生成的,如果希望自己定制的话,可能就必须设置self.options.nav=false了,并且为导航元素绑定事件比如
$(function() {
var slider = $('.banner').unslider({nav: false});
var self = slider.data('unslider');
$('.myNav > li').each(function(key) {
$(this).on('click', function() {
var $me = $(this).addClass('activClass');
$me.siblings().removeClass('activeClass');
//重要
self.animate(key);
});
});
});
3.2.5 initArrows方法
self.initArrows = function() {
//如果指定arrows是true,则重新对self.options.arrows赋值
//弱类型语言就是随意,啊!
if(self.options.arrows === true) {
self.options.arrows = self.defaults.arrows;
}
// self.defaults.arrows是默认设计好了arrows需要的元素的
$.each(self.options.arrows, function(key, val) {
// insertAfter返回是$(val)
// 所以可以直接push到self.$arrows
// self.$arrows是之前定义好的空数组
self.$arrows.push(
$(val).insertAfter(self.$context).on('click' + self.eventSuffix, self[key])
);
});
};
3.2.6 initKeys方法
self.initKeys = function() {
//默认参数self.defaults.keys === true并不能成立
//这里条件通过只能是外部传入的参数覆盖的
//外部参数没有覆盖的情况,后续依然使用默认的参数
if(self.options.keys === true) {
self.options.keys = self.defaults.keys;
}
//使用默认参数
$(document).on('keyup' + self.eventSuffix, function(e) {
$.each(self.options.keys, function(key, val) {
if(e.which === val) {
$.isFunction(self[key]) && self[key].call(self);
}
});
});
};
按照默认的参数,最终绑定键盘事件的时候,我们看到的是
$.each({prev: 37, next: 39}, function(key, val){
...
$.isFunction(self[key]) && self[key].call(self);
});
最终调用的还是self.next和self.prev方法。
3.2.7 initInfinite方法
// Infinite scrolling is a massive pain in the arse
// so we need to create a whole bloody function to set
// it up. Argh.
self.initInfinite = function() {
var pos = ['first', 'last'];
$.each(pos, function(index, item) {
self.$slides.push.apply(
self.$slides,
// Exclude all cloned slides and call .first() or .last()
// depending on what `item` is.
self.$slides.filter(':not(".' + self._ + '-clone")')[item]()
// Make a copy of it and identify it as a clone
.clone().addClass(self._ + '-clone')
// Either insert before or after depending on whether we're
// the first or last clone
['insert' + (index === 0 ? 'After' : 'Before')](
// Return the other element in the position array
// if item = first, return "last"
self.$slides[pos[~~!index]]()
)
);
});
};
这个方法默认情况下是不会被调用的,需要在外部传入infinite参数才会被调用,如
$(function() {
$('.banner').unslider({infinite: true});
});
逐行来阅读这个方法的代码:
1) 首先定义数组
var pos = ['first', 'last'];
2) 遍历数组
$.each(pos, function(index, item) {
});
3) 向self.$slides插入克隆的轮播页面jQuery对象
self.$slides.push.apply(
self.$slides,
//clone jQuery object here
);
首先self.$slides是在self.init方法初始化的,self.$slides = self.$container.children(self.options.selectors.slides);。
self.$slides是一个jQuery对象,为了向self.$slides插入(克隆的轮播页面的)jQuery对象,
借用了self.$slides的方法。这里似乎是可以改成:
self.$slides.push(
//clone jQuery object here
);
有待进一步验证。
4) 过滤获得需要克隆的元素(的jQuery对象)
self.$slides.filter(':not(".' + self._ + '-clone")')[item]()
其中item即为first或者last,第一次我们需要克隆第一个,第二次我们需要克隆最后一个;克隆第一个插入到self.$slides的最后位置,克隆最后一个插入到self.$slides的开头位置。如果不加过滤的话,容易导致一个问题,但是第二次克隆时通过类似self.$slides.last()方法我们获取到的是第一次克隆的结果,所以unslider利用了self._ + 'clone'类做了区分。
5) 执行克隆并加上unslider-clone类
.clone().addClass(self._ + '-clone')
6) 执行插入
['insert' + (index === 0 ? 'After' : 'Before')](
// relative jQuery object to insertBefor or insertAfter
)
首先判断是需要执行insertAfter还是insertBefore方法,接5)执行这个方法的是克隆后的jQuery对象,可以理解下面的伪代码:
var cloneJQ;
cloneJQ.insertAfter(anotherJQ);
当index === 0时,执行第一次克隆从原来self.$slides的第一个克隆插入到self.$slides结尾的位置,所以第一次应该是执行insertAfter方法。
7) 找到相对的轮播页面jQuery对象
self.$slides[pos[~~!index]]()
不管是执行insertAfter还是insertBefore都是一个相对的jQuery对象;第一次克隆我们需要插入的位置是结尾,第二次插入的位置是开头。即
index:0 --> self.$slides.last()
index:1 --> slef.$slides.first()
再来看pos[~~!index],这个目的是从pos数组获取某个元素,关键看~~!index的结果。举些例子:
~~!0 //1
~~!1 //0
~~!2 //0
~~!-1 //0
这个技巧,啊!
3.2.8 calculateSlides方法
// Set up the slide widths to animate with
// so the box doesn't float over
self.calculateSlides = function() {
self.total = self.$slides.length;
// Set the total width
if(self.options.animation !== 'fade') {
var prop = 'width';
if(self.options.animation === 'vertical') {
prop = 'height';
}
self.$container.css(prop, (self.total * 100) + '%').addClass(self.prefix + 'carousel');
self.$slides.css(prop, (100 / self.total) + '%');
}
};
判断轮播的方向是垂直还是水平,设置容器的高度或宽度是self.$slides个数的倍数;设置每个轮播页面元素的高度或者宽度,由于是相对的,所以轮播页面的高度或宽度理论是没有改变的。
//变化前
ul 100px
li 100px
共有4个li
//变化后
ul 100 X 4 = 400 px
li ul.width / 4 = 100 px
另外给容器设置了unslider-carousel类,这个类的作用暂且忽略。
3.2.9 start方法
self.start = function() {
self.interval = setTimeout(function() {
// Move on to the next slide
self.next();
// If we've got autoplay set up
// we don't need to keep starting
// the slider from within our timeout
// as .animate() calls it for us
}, self.options.delay);
return self;
};
开始定时器。
3.2.10 stop方法
self.stop = function() {
clearTimeout(self.interval);
return self;
};
清除定时器。
3.2.11 next方法
self.next = function() {
//下一个
var target = self.current + 1;
// 如果大于总数,就回到开始
if(target >= self.total) {
target = 0;
}
//交给self.animate方法去完成
return self.animate(target, 'next');
};
3.2.12 prev方法
self.prev = function() {
return self.animate(self.current - 1, 'prev');
};
和self.next()方法类是,self.animate方法能够支持`animate(-1, 'prev')的写法,不需要出入target参数。
3.2.13 animate方法
虽然方法名叫animate但是其实并没有真正动起来,最终还是交给三种不同轮播效果的animate开头的函数,如animateHorizontal、animateVertical和animateFade。
// Despite the name, this doesn't do any animation - since there's
// now three different types of animation, we let this method delegate
// to the right type, keeping the name for backwards compat.
self.animate = function(to, dir) {
// Animation shortcuts
// Instead of passing a number index, we can now
// use .data('unslider').animate('last');
// or .unslider('animate:last')
// to go to the very last slide
if(to === 'first') to = 0;
if(to === 'last') to = self.total;
// Don't animate if it's not a valid index
if(isNaN(to)) {
return self;
}
if(self.options.autoplay) {
self.stop().start();
}
//设置了目标序号
self.setIndex(to);
//触发unslider.change事件
//个人觉得自定义的事件最好不要用.号分隔
self.$context.trigger(self._ + '.change', [to, self.$slides.eq(to)]);
// Delegate the right method - everything's named consistently
// so we can assume it'll be called "animate" +
var fn = 'animate' + $._ucfirst(self.options.animation);
// Make sure it's a valid animation method, otherwise we'll get
// a load of bug reports that'll be really hard to report
if($.isFunction(self[fn])) {
//self.current已经在setIndex方法中修改了
self[fn](self.current, dir);
}
return self;
};
3.2.14 setIndex方法
这个方法会修改self.current属性。
self.setIndex = function(to) {
//处理负数的情况
if(to < 0) {
to = self.total - 1;
}
//current不能超过self.total -1
self.current = Math.min(Math.max(0, to), self.total - 1);
//如果支持导航,需要将相应的导航元素设置active类
if(self.options.nav) {
self.$nav.find('[data-slide="' + self.current + '"]')._active(self.options.activeClass);
}
//设置选中的轮播页面的active类
self.$slides.eq(self.current)._active(self.options.activeClass);
return self;
};
self.$nav和self.$slides都有调用$.fn._active,这个类能够做到的是,将自己jQuery对象增加active类,并将所有兄弟元素对象移除active类。
3.3 轮播动画
这一版的unslider支持三种类型的动画,左右、垂直方向轮播、还有就是fade(翻译成闪现合理么?),分别对应animateHorizotal、animateVertical和animateFade三种方法。
3.3.1 animateHorizontal方法
self.animateHorizontal = function(to) {
var prop = 'left';
// Add RTL support, slide the slider
// the other way if the site is right-to-left
if(self.$context.attr('dir') === 'rtl') {
prop = 'right';
}
//见前面self.initInfinite解释
//如果self.options.infinite是true,在开始和结束位置都会多增加克隆的页面元素
//所以这里需要减去相应的宽度
if(self.options.infinite) {
// So then we need to hide the first slide
self.$container.css('margin-' + prop, '-100%');
}
//委托给slide方法,to是序号
return self.slide(prop, to);
};
animateHorizontal是由animate调用的,原来的参数中animate(to, dir),to被修正为目标序号并设置到self.current变量后,调用animateHorizontal方法传入animateHorizontal(self.current,dir),到了这里似乎dir参数被丢弃了(不明白)。
3.3.2 animateVertical方法
self.animateVertical = function(to) {
self.options.animateHeight = true;
// Normal infinite CSS fix doesn't work for
// vertical animation so we need to manually set it
// with pixels. Ah well.
//减去自身的高度
if(self.options.infinite) {
self.$container.css('margin-top', -self.$slides.outerHeight());
}
return self.slide('top', to);
};
3.3.3 animateFade方法
3.3.4 slide方法
真正轮播页面的方法。
self.slide = function(prop, to) {
// If we want to change the height of the slider
// to match the current slide, you can set
// {animateHeight: true}
if(self.options.animateHeight) {
self._move(self.$context, {height: self.$slides.eq(to).outerHeight()}, false);
}
// For infinite sliding we add a dummy slide at the end and start
// of each slider to give the appearance of being infinite
// 处理参数infinite是true的情况
if(self.options.infinite) {
var dummy;
// Going backwards to last slide
if(to === self.total - 1) {
// We're setting a dummy position and an actual one
// the dummy is what the index looks like
// (and what we'll silently update to afterwards),
// and the actual is what makes it not go backwards
dummy = self.total - 3;
to = -1;
}
// Going forwards to first slide
if(to === self.total - 2) {
dummy = 0;
to = self.total - 2;
}
// If it's a number we can safely set it
if(typeof dummy === 'number') {
self.setIndex(dummy);
// Listen for when the slide's finished transitioning so
// we can silently move it into the right place and clear
// this whole mess up.
self.$context.on(self._ + '.moved', function() {
if(self.current === dummy) {
self.$container.css(prop, -(100 * dummy) + '%').off(self._ + '.moved');
}
});
}
}
// We need to create an object to store our property in
// since we don't know what it'll be.
var obj = {};
// Manually create it here
obj[prop] = -(100 * to) + '%';
// And animate using our newly-created object
return self._move(self.$container, obj);
};
处理self.options.infinite参数为true的情况时,源码中有些指定的数字,不知道是何依据。
内部定义的obj,最后传给self._move方法,轮播功能进一步委托给self._move来完成。做下假定,如果我们使用默认参数,即水平轮播,并假设需要轮播到第二个页面,此时后面的代码最终效果如下:
var obj = {};
obj["left"] = -(100 * 1) + '%';
return self._move(self.$container, obj);
3.4 事件绑定
unslider自定义了几种事件,包括unslider.change、unslider.ready和unslider.moved等,而在绑定导航元素的点击事件时使用了命名空间的形式。命名空间由self.eventSuffix指定。
3.4.1 unslider自定义事件
请参考API文档。
3.4.3 命名空间的click事件
参考self.initNav部分说明。
3.5 其他方法
3.5.1 $.Unslider._move方法
self._move = function($el, obj, callback, speed) {
//回调处理
if(callback !== false) {
callback = function() {
self.$context.trigger(self._ + '.moved');
};
}
//调用$.fn._move方法
return $el._move(obj, speed || self.options.speed, self.options.easing, callback);
};
$el是有animateHorizontal方法调用self._move传入的self.$container,即对应的ul层。
3.5.2 $.fn._move方法
$.fn._move = function() {
//停止所有动画,参照jQuery的animate说明文档
this.stop(true, true);
//如果没有添加velocity支持,最终动画还是由$.fn.animate方法来完成
return $.fn[$.fn.velocity ? 'velocity' : 'animate'].apply(this, arguments);
};
根据前面的说明,最终交由$.fn.animate方法来挖成动画。按照之前的假设,此时这里的效果如下面代码所示:
var obj = {"left": "-100%"};
return $.fn.animate.apply(self.$container, arguments);
//arguments有obj, speed, callback等参数
至此,整个轮播过程的调用过程就分析完毕。