根据Bootstrap中文网的介绍,Unslider一个超小的 jQuery轮播(slider)插件,参照这个汉化版的介绍页面,这个插件有你须要的优势,可是本文是抱着学习的态度,学习如何实现轮播插件,因此有些细节可能有所忽略。css
参照Bootstrap中文网提供的介绍页面,或者参照官网的介绍都是能够,虽然unslider已经升级了版本,可是使用方式(API接口)仍是没有改变。jquery
对于HTML结构的要求只须要提供相似如下结构便可:git
<div class="banner"> <ul> <li>This is a slide.</li> <li>This is another slide.</li> <li>This is a final slide.</li> </ul> </div>
而后引入jquery.js
和unslider.js
两个文件,便可以在DOM加载完执行github
$(function() { $('.banner').unslider(); });
我取汉化版介绍页面的元素,使用最新版的unslider.js
,调用unslider()
,比较页面元素有什么变化。正则表达式
源代码数组
<div class="banner"> <ul> <li style="background-image: url('img/sunset.jpg');"> <div class="inner"> <h1>The jQuery slider that just slides.</h1> <p>就是这个不到3kb的插件!没有奇特的特效或无用的标签。</p> <a class="btn" href="#download">下载</a> </div> </li> <li style="background-image: url('img/wood.jpg');"> <div class="inner"> <h1>Fluid, flexible, fantastically minimal.</h1> <p>Use any HTML in your slides, extend with CSS. You have full control.</p> <a class="btn" href="#download">下载</a> </div> </li> <li style="background-image: url('img/subway.jpg');"> <div class="inner"> <h1>开源</h1> <p>Unslider的全部源码都托管在GitHub上。</p> <a class="btn" href="//github.com/idiot/unslider">参与</a> </div> </li> <li style="background-image: url('img/shop.jpg');"> <div class="inner"> <h1>Uh, that’s about it.</h1> <p>I just wanted to show you another slide.</p> <a class="btn" href="#download">下载</a> </div> </li> </ul> </div>
使用插件后的效果(有所节省)缓存
<div class="unslider"> <div class="banner unslider-horizontal" style="overflow: hidden;"> <ul class="unslider-wrap unslider-carousel" style="width: 400%; left: 0%;"> <li style="width: 25%; class="unslider-active"> </li> <li style="width: 25%; class=""> </li> <li style="width: 25%; class=""> </li> <li style="width: 25%; class=""> </li> </ul> </div> <a class="unslider-arrow next">Next</a> <a class="unslider-arrow prev">Prev</a> <nav class="unslider-nav"> <ol> <li data-slide="0" class="unslider-active">1</li> <li data-slide="1" class="">2</li> <li data-slide="2" class="">3</li> <li data-slide="3" class="">4</li> </ol> </nav> </div>
能够发现使用插件后,会在.banner
上封装<div class="unslider"></div>
,而且对.banner
设置样式不让子元素溢出;在ul
上设置宽度是li
元素的整数倍,li
元素的全部兄弟元素平均结果(100/4);还加上next
和prev
元素,加上了nav
导航。闭包
ul
是相对于.banner
定位的,虽然宽度是大于100%,可是.banner
是不会被ul
撑开的;而在ul
上配置width
和left
参数,能够控制显示ul
的起始位置,left:-100%
至关于ul
向左飘过去了100%
,通俗点说:app
父元素.banner
只能让ul
显示一个身位,可是ul
膨胀了,实际它有4个身位,相对于.banner
定位,默认left:0%
时,
至关于显示0-1
身位的ul
,为了显示第二个身位的ul
,就必须将ul
往左移,让它显示1-2
位置的ul
的,因此此时设置left: -100%
,
以此类推。
$.fn.unslider
方法$.fn.unslider
方法是在jQuery原型链定义的方法,jQuery对象天然可以调用这个方法。前面的例子中咱们是直接调用的,并无传入参数,事实上$.fn.unslider
还能够接收相似这样的参数:$(".banner").unslider("fn:arg1,arg2")
。最终调用在某个位置定义的fn
函数,参数是arg1
和arg2
。dom
$.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
来完成。
$.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
会指向#id
DOM元素,固然目前$.Unslider
静态方法是没法被jQuery对象直接调用的。
$.Unslider
总体结构总体结构:
$.Unslider = function(context, options) { var self = this; //插件标识 self._ = 'unslider'; //默认参数 self.defaults = { }; /** * 参照生成后的页面元素作个类比 * self.$parent => <div class="unslider"></div> * self.$context => <div class="banner"></div> * self.$container => <ul class="unslider-wrap"></ul> * self.slides => <li></li> */ //备份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);
使用方式上可能就有点不一样了。
$.Unslider
源码分析//开始重要的源码分析
$.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: '<a class="' + self._ + '-arrow prev">Prev</a>', next: '<a class="' + self._ + '-arrow next">Next</a>' }, // 方向 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 }; ... };
初始化方法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
等方法。接下来会逐个分析它们。
self.setup = function() { //给轮播容器的复层(.banner)作封装 self.$context.addClass(self.prefix + self.options.animation).wrap('<div class="' + self._ + '" />'); //备份容器的父层,即刚才的封装层 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
的一部分。
self.initNav = function() { // HTML5到导航标签 var $nav = $('<nav class="' + self.prefix + 'nav"><ol /></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('<li data-slide="' + key + '">' + label + '</li>'); }); // 插入到$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); }); }); });
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]) ); }); };
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
方法。
// 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
这个技巧,啊!
// 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
类,这个类的做用暂且忽略。
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; };
开始定时器。
self.stop = function() { clearTimeout(self.interval); return self; };
清除定时器。
self.next = function() { //下一个 var target = self.current + 1; // 若是大于总数,就回到开始 if(target >= self.total) { target = 0; } //交给self.animate方法去完成 return self.animate(target, 'next'); };
self.prev = function() { return self.animate(self.current - 1, 'prev'); };
和self.next()方法类是,self.animate方法可以支持`animate(-1, 'prev')的写法,不须要出入target参数。
虽然方法名叫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; };
这个方法会修改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类。
这一版的unslider支持三种类型的动画,左右、垂直方向轮播、还有就是fade(翻译成闪现合理么?),分别对应animateHorizotal
、animateVertical
和animateFade
三种方法。
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
参数被丢弃了(不明白)。
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); };
略
真正轮播页面的方法。
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);
unslider自定义了几种事件,包括unslider.change、unslider.ready和unslider.moved等,而在绑定导航元素的点击事件时使用了命名空间的形式。命名空间由self.eventSuffix
指定。
请参考API文档。
参考self.initNav
部分说明。
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
层。
$.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等参数
至此,整个轮播过程的调用过程就分析完毕。
略
略