unslider源码分析

发布时间:2019-06-06 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了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.jsunslider.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);还加上nextprev元素,加上了nav导航。

ul是相对于.banner定位的,虽然宽度是大于100%,但是.banner是不会被ul撑开的;而在ul上配置widthleft参数,可以控制显示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函数,参数是arg1arg2

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 => <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);

使用方式上可能就有点不同了。

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: '<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
    };
    ...
};

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只是初始化过程中的一个入口,它还需要其他初始化方法来帮助完成其他业务逻辑,包括setupinitNavinitArrowsinitKeysinitInfinitecalculateSlides等方法。接下来会逐个分析它们。

3.2.3 setup方法

标签:CSSHTML