脚本宝典收集整理的这篇文章主要介绍了unslider源码分析,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
根据Bootstrap中文网的介绍,Unslider一个超小的 jQuery轮播(slider)插件,参照这个汉化版的介绍页面,这个插件有你需要的优点,但是本文是抱着学习的态度,学习如何实现轮播插件,所以有些细节可能有所忽略。
1. 如何使用
参照Bootstrap中文网提供的介绍页面,或者参照官网的介绍都是可以,虽然unslider已经升级了版本,但是使用方式(API接口)还是没有改变。
对于HTML结构的要求只需要提供类似以下结构即可:
<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加载完执行
$(function() {
$('.banner').unslider();
});
我取汉化版介绍页面的元素,使用最新版的unslider.js
,调用unslider()
,比较页面元素有什么变化。
源代码
" title="" data-original-title="复制">
<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%
,通俗点说:
父元素.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
。
$.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
等方法。接下来会逐个分析它们。