偶遇Unslider

今天想在网页上放个焦点图,最开始用myfocus,出现不少问题,但我不想在改造它上面花太多时间,就打算直接使用Bootstrap的Carousel插件,结果在Bootstrap中文网上偶遇了这么一个很纯粹的幻灯插件——Unslider,真是相见恨晚啊!为了留档备查,我先记录一下如何使用,再分析一下源码。新手学习,难免有误,希望路过的大神指点一二,不甚感激!

一、如何使用

第1步:导入类库

Unslider是基于Jquery,自然要先将Jquery库导入

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="http://unslider.com/unslider.js"></script>

第2步:写HTML结构,即幻灯片列表

<div class="banner">
    <ul>
        <li style="background:url(1.jpg)">first slide.</li>
        <li style="background:url(2.jpg)">second slide.</li>
        <li style="background:url(3.jpg)">third slide.</li>
    </ul>
</div>


第3步:添加样式,这里先不管漂亮,只放官网上说必须有的骨架

.banner { position: relative; overflow: auto; }
.banner li { list-style: none; }
.banner ul li { float: left; }

第4步:调用unslider()方法让幻灯片列表滚动起来,参数就先默认吧

$(function() {
    $('.banner').unslider();
});

好了,到这里幻灯片就如预期滚动起来了。但是不美观,为了更加灵活定制自己想要的效果,我要潜入源代码瞧一瞧。

二、源码分析

/**
 *   Unslider by @idiot and @damirfoy
 *   Contributors:
 *   - @ShamoX
 *
 */

(function($, f) {
    var Unslider = function() {
        //  克隆对象
        var _ = this;

        //  设置一些默认参数
        _.o = {
            speed: 500,     // 动画过渡的速度(毫秒),如果不需要过渡效果就设置为false
            delay: 3000,    // 每张幻灯片的间隔时间(毫秒), 如果不是自动播放就设置为false
            init: 0,        // 初始化延迟时间(毫秒),如果不需要延迟就设置为false
            pause: !f,      // 当鼠标指针浮动在当前区域内时是否暂停自动播放
            loop: !f,       // 是否无尽循环播放
            keys: f,        // 是否开启键盘导航
            dots: f,        // 是否显示导航点
            arrows: f,      // 是否显示向前和向后的箭头
            prev: '←',     // 向前按钮中的显示文字(或html片段)
            next: '→',     // 向后......
            fluid: f,       // 是否宽度自适应
            starting: f,    // 在每个动画前调用的函数
            complete: f,    // 在每个动画之后调用的函数
            items: '>ul',   // 幻灯片的容器选择器
            item: '>li',    // 需要滚动的选择器
            easing: 'swing',// 动画的缓动函数(easing function)
            autoplay: true  // 是否允许自动播放
        };

        _.init = function(el, o) {
            //  将我们在外部调用时设置的参数覆盖掉默认参数
            _.o = $.extend(_.o, o);

            _.el = el;
            _.ul = el.find(_.o.items);//返回ul元素集合
            _.max = [el.outerWidth() | 0, el.outerHeight() | 0];//保存一下幻灯片div容器的宽和高
            _.li = _.ul.find(_.o.item).each(function(index) {
                var me = $(this),
                    width = me.outerWidth(),
                    height = me.outerHeight();

                //  记录最大幻灯片的宽高
                if (width > _.max[0]) _.max[0] = width;
                if (height > _.max[1]) _.max[1] = height;
            });


            //  申请一些临时变量
            var o = _.o,
                ul = _.ul,
                li = _.li,
                len = li.length;//li元素个数

            //  当前索引,或者叫页码更容易理解吧,源代码中写了“Current indeed”,应该是“index”吧
            _.i = 0;

            //  设置幻灯片div容器的样式,高度初始化为第一个li的高度
            el.css({width: _.max[0], height: li.first().outerHeight(), overflow: 'hidden'});

            //  设置ul元素的位置和宽度,宽度的公式是(li元素的个数乘以100)%,我的例子中就是300%
            ul.css({position: 'relative', left: 0, width: (len * 100) + '%'});
            if(o.fluid) {
                li.css({'float': 'left', width: (100 / len) + '%'});//自适应宽度时,li元素的宽度就是把ul的宽度平均分成len份
            } else {
                li.css({'float': 'left', width: (_.max[0]) + 'px'});//不是自适应时,li元素的宽度是最大的幻灯片的宽度
            }

            //  在init毫秒后开启自动播放
            o.autoplay && setTimeout(function() {
                if (o.delay | 0) {
                    _.play();

                    if (o.pause) {
                        el.on('mouseover mouseout', function(e) {
                            _.stop();//鼠标经过时暂停
                            e.type == 'mouseout' && _.play();//鼠标离开时播放
                        });
                    };
                };
            }, o.init | 0);

            //  键盘事件处理
            if (o.keys) {
                $(document).keydown(function(e) {
                    var key = e.which;

                    if (key == 37)
                        _.prev(); // 左箭头按键
                    else if (key == 39)
                        _.next(); // 右箭头按键
                    else if (key == 27)
                        _.stop(); // Esc
                });
            };

            //  显示导航点
            o.dots && nav('dot');

            //  显示箭头
            o.arrows && nav('arrow');

            //  使幻灯片div容器宽度自适应
            if (o.fluid) {
                $(window).resize(function() {
                    _.r && clearTimeout(_.r);

                    _.r = setTimeout(function() {
                        var styl = {height: li.eq(_.i).outerHeight()},
                            width = el.outerWidth();

                        ul.css(styl);
                        //这一串真是绕,其实就是计算div占父窗口的宽度原始比例,然后记录到styl中
                        styl['width'] = Math.min(Math.round((width / el.parent().width()) * 100), 100) + '%';
                        el.css(styl);//重新设置幻灯片div容器的宽度为比例而不是像素值,这样就能达到自适应的目的了
                        li.css({ width: width + 'px' });//设置li的绝对宽度,以防因div被自适应了而挤压或拉伸了li造成内容扭曲(如有误请大神指教)
                    }, 50);//每次父窗口改变大小时,幻灯片div容器延迟50毫秒后再跟着自适应大小,请大神告诉我这样做的目的仅仅是为了效果更自然么
                }).resize();//强制执行resize事件,使得自适应特性在最开始时就被设置好了
            };

            //  自定义move事件,这一段不太懂,求大神指点
            if ($.event.special['move'] || $.Event('move')) {
                //  为幻灯片div元素绑定movestart、move、moveend事件
                el.on('movestart', function(e) {
                    if ((e.distX > e.distY && e.distX < -e.distY) || (e.distX < e.distY && e.distX > -e.distY)) {
                        e.preventDefault();//鼠标位置不在当前区域时取消事件的默认动作(我猜的,关键是不知道distX这几个的准确含义)
                    }else{
                        el.data("left", _.ul.offset().left / el.width() * 100);
                    }
                }).on('move', function(e) {
                        var left = 100 * e.distX / el.width();
                        var leftDelta = 100 * e.deltaX / el.width();
                        _.ul[0].style.left = parseInt(_.ul[0].style.left.replace("%", ""))+leftDelta+"%";

                        _.ul.data("left", left);
                    }).on('moveend', function(e) {
                        var left = _.ul.data("left");//
                        if (Math.abs(left) > 30){
                            var i = left > 0 ? _.i-1 : _.i+1;
                            if (i < 0 || i >= len) i = _.i;
                            _.to(i);
                        }else{
                            _.to(_.i);
                        }
                    });
            };

            return _;
        };

        //  播放指定索引的幻灯片
        _.to = function(index, callback) {
            if (_.t) {
                _.stop();
                _.play();
            }
            var o = _.o,
                el = _.el,
                ul = _.ul,
                li = _.li,
                current = _.i,
                target = li.eq(index);
            //在动画之前执行的函数,我的例子里都没有,可以忽略它们
            $.isFunction(o.starting) && !callback && o.starting(el, li.eq(current));

            //  如果(一张幻灯片也没有或者索引无效),并且不是循环播放,就啥也不做,我觉得这样不好,因为to这个函数就只能在循环播放状态下工作了
            if ((!target.length || index < 0) && o.loop == f) return;

            //  检查索引是否有效,超出时设置为0,即第一张幻灯片
            if (!target.length) index = 0;
            if (index < 0) index = li.length - 1;//索引负数时设置为最后一张幻灯片
            target = li.eq(index);//获取目标元素

            var speed = callback ? 5 : o.speed | 0,//执行回调函数后返回的是真则speed设为5,如果没有回调函数或返回假则设置为o.speed
                easing = o.easing,
                obj = {height: target.outerHeight()};

            if (!ul.queue('fx').length) {//确保没有为ul元素添加函数队列,应该是为了防止上一次动作还没有完成吧
                //  设置对应导航点的高亮
                el.find('.dot').eq(index).addClass('active').siblings().removeClass('active');
                //  改变幻灯片div容器的高度为目标元素的高度,并把ul的位置向左移动(index*100%),使目标元素正好在幻灯片div容器区域
                el.animate(obj, speed, easing) && ul.animate($.extend({left: '-' + index + '00%'}, obj), speed, easing, function(data) {
                    _.i = index;//移动结束之后更新一下当前索引
                    //动画结束之后执行的函数,我的例子中也没有,忽略它们
                    $.isFunction(o.complete) && !callback && o.complete(el, target);
                });
            };
        };

        //  每隔delay毫秒自动播放
        _.play = function() {
            _.t = setInterval(function() {
                _.to(_.i + 1);//这里就加了1个索引号,具体的处理都封装在了to方法中
            }, _.o.delay | 0);
        };

        //  停止自动播放
        _.stop = function() {
            _.t = clearInterval(_.t);
            return _;
        };

        //  向后翻一张
        _.next = function() {
            return _.stop().to(_.i + 1);
        };
        //  向前翻一张
        _.prev = function() {
            return _.stop().to(_.i - 1);
        };

        //  创建导航点和箭头
        function nav(name, html) {
            if (name == 'dot') {
                html = '<ol class="dots">';
                $.each(_.li, function(index) {
                    html += '<li class="' + (index == _.i ? name + ' active' : name) + '">' + ++index + '</li>';
                });
                html += '</ol>';
                /*整理一下,在我的例子中就是这副摸样
                  <ol class="dots">           
                  <li class="dot active">0</li>
                  <li class="dot">1</li>
                  <li class="dot">2</li>
                  </ol>
                */
            } else {
                html = '<div class="';
                html = html + name + 's">' + html + name + ' prev">' + _.o.prev + '</div>' + html + name + ' next">' + _.o.next + '</div></div>';
                /*也整理一下
                  <div class="arrows">
                    <div class="arrow prev">←</div>
                    <div class="arrow next">→</div>
                  </div>
                */
            };
            //先给幻灯片div容器元素加上has-dots或arrows的class,再把上面组织好的元素追加为子元素,并给该子元素添加click事件处理函数
            _.el.addClass('has-' + name + 's').append(html).find('.' + name).click(function() {
                var me = $(this);
                me.hasClass('dot') ? _.stop().to(me.index()) : me.hasClass('prev') ? _.prev() : _.next();
            });
        };
    };

    //  将unslider方法扩展到jQuery对象,使任意jQuery对象都能够直接访问该方法,就像上面那样:$('.banner').unslider();
    $.fn.unslider = function(o) {
        var len = this.length;

        //  遍历li元素集
        return this.each(function(index) {
            var me = $(this),
                key = 'unslider' + (len > 1 ? '-' + ++index : ''),
                instance = (new Unslider).init(me, o);

            //  给div元素添加数据
            me.data(key, instance).data('key', key);
        });
    };

    Unslider.version = "1.0.0";
})(jQuery, false);


三、定制自己需要的幻灯片

终于看完大部分了。好了,通过源码至少我知道了该怎样去控制外观,现在我要动手啦,心里有点小激动呢~

我选择Unslider是因为它是如此纯粹,正如我想要的那样,啥也可以不要,只要能自动循环播放、用导航点控制翻页就满足我的要求了。

因为默认就是自动循环播放的,所以我只要设置一个显示导航点的参数即可,然后锦上添花也支持一下响应式吧:

$(function() {
    $('.banner').unslider({
        dots: true,               //  显示导航点
        fluid: true               //  支持响应式设计
    });
});

通过源码我们知道unslider为我们写好了导航点的文档结构、并取好了类名,但并没有设置样式,所以只是传个参数是木有用滴,要自己写样式才能看见那些可爱的点点:

*{margin: 0;padding: 0;}
.banner { position: relative; top:0;overflow: auto; }
.banner li { list-style: none; }
.banner ul li { float: left; }
.dots{width:66px;position:absolute;bottom:10px;left:50%;margin-left:-27px;}
.dot{
    display:inline-block;
    width: 10px;
    height: 10px;
    font-size: 0px;
    line-height: 0px;
    margin-right: 12px;
    cursor: pointer;
    background-color: #ffffff;
    border-radius: 8px;
}
.dot.active{background-color: #cce8cf;}

大功告成!

四、总结

对jquery的事件机制还不熟悉,遇到自定义事件啥的就犯晕了,这块知识还要多加学习。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值