看了几个网上的轮播图例子,然后理解了其中的原理,自己写了一个插件,实现了无缝轮播。
先来说一下实现原理:
我们需要实现下图所示的轮播图,它具有以下功能:
A. 每隔一定的时间图片自动播放;
B. 点击对应的圆点跳转到对应的图片;
C. 点击两边的按钮可以播放上一张图或者下一张图;
D. 当鼠标悬停在图片上方时暂停播放。
首先要实现功能A,这里已默认5张图为例,真实的图片肯定不是五张,这个想必大家都已知道,如果后台仍然使用5张图就不可能实现真正的无缝了。而在此我们不仅要实现自动播放,还要实现按钮功能,所以我们还需有第一张图跳转到最后一张图的功能,所以需要7张图片。然后要实现图片轮播的效果就要让图片在一条线上,使用float可以轻松实现(这里要注意,我们整个框架是:div>ul>li>img,div和li,img肯定是固定宽高,而ul就需要内容撑开宽度了,我们不知道图片的数量,如果固定了宽度,就会造成图片不再一条线上);接着要让他动起来,我们首先想到的是通过改变ul的left值来改变图片的显示,当然这里我也用的是left,但是如果使用left会降低效率,不如用translateX()更加高效。不过那个还没有做出来,等后面做出来了再修改一下。自动播放就是把这个功能放在一个setInterval()里面就可以了。
其次要实现功能B,看到这个功能我们想到的是如何将圆点与图片连接起来,还有处理圆点的点击事件。针对前一个问题,我的解决方法是给span(圆点)添加data-index属性,并对其编号,这样我们通过点击获得的编号就可以知道要跳转到哪张图片了。针对后一个问题,我想到了可以给span外面包一层div,然后给div添加点击事件,通过事件冒泡得到事件源(即target属性),然后获取事件源的data-index即可。
然后是功能C,这个就比较简单了,右边的按钮就是触发一次播放的动画,左边的按钮就是反方向触发一次放的动画。
最后是功能D,可以通过hover()清除setInterval(),移除后重新创建一个。
考虑到插件的兼容性,即因为图片数量是未知的,所以小圆点span标签需要动态创建,同时对两个按钮也动态创建。
先附上结构
<div class="wrapper"> <div class="box"> <ul class="carousel-inner"> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> <li class="carousel-item"><img src="./images/-jpg" alt=""></li> </ul> </div> </div>
|
首先我们扩展一个封装函数sowingMap(),并在函数内通过jq的extend方法处理参数,如果用户没有传进来参数就使用默认参数。
(function() { $.fn.sowingMap = function(option) { var args = $.extend({ count : 2, time : 3000 }, option); } })(jQuery);
|
为了结构看起来清楚明了,我另外创建了一个“构造函数”Init(),并在此函数的原型链上编程。
function Init(ele, args) { if (args.count > 1) { this.init(ele, args); } else { alert('请输入正确的图片数量') } } Init.prototype = {}
|
在原型链上,首先写一个入口函数init()处理传进来的参数,并初始化后面需要用到的dom节点,调用主要函数。
init : function(ele, args) { this.ele = ele, this.count = args.count, this.time = args.time, this.index = 0, this.oUl = this.ele.find('.carousel-inner'), this.oLi = this.oUl.find('.carousel-item'), this.createSpan(), this.handleUl(), this.automatic(), this.eventBind() }
|
然后写一个handleUl()方法,用来处理图片,即复制最后一张图片并将其接在第一位,复制第一张图片并将其接在最后一位。
handleUl : function() { var last = this.oLi.eq(0).clone(), first = this.oLi.eq(this.count - 1).clone(), width = this.ele.width(), left = - (this.index + 1) * this.ele.width(); this.oUl.prepend(first).append(last).css('left', left + 'px'); this.oUl.width((this.count + 2 ) * width); }
|
接着写一个createSpan()方法,用来动态创建span标签以及btn按钮。
createSpan : function() { var str = '<div class="carousel-indicators">'; for (var i = 0; i < this.count; i ++) { str += '<span data-index="' + i + '"></span>'; } str += '</div><span class="carousel-btn carousel-prev-btn"></span>' + '<span class="carousel-btn carousel-next-btn"></span>'; this.ele.append(str); this.ele.find('.carousel-indicators span').eq(this.index).addClass('active') }
|
接着就是处理动画了,首先写一个ianimate()方法,用来处理动画,在这里面要注意,当图片动画结束后我们要处理圆点的效果以及图片的衔接问题。
ianimate : function(ileft) { var tleft = this.oUl.position().left, i = this; i.oUl.animate({ left : ileft + tleft }, function() { var left = i.oUl.position().left, width = i.ele.width(); ileft > 0 && left > -width && i.oUl.css('left', - i.count * width); ileft < 0 && left < - i.count * width && i.oUl.css('left', -width); i.index = parseInt(- left / width) - 1; i.index = i.index > i.count - 1 ? 0 : i.index; i.index = i.index < 0 ? i.count - 1 : i.index; i.renBtns(); }) }
|
接着是renBtns()方法,通过给对应的span添加类名来改变样式。
renBtns : function() { this.ele.find('.carousel-indicators span').removeClass("active").eq(this.index).addClass('active') }
|
当动画做好后就要去触发动画了,写一个eventBind()方法来放置圆点以及按钮的点击事件还有外层div的hover事件。
eventBind : function() { var i = this, prev = this.ele.find(".carousel-prev-btn"), next = this.ele.find(".carousel-next-btn"), span = this.ele.find(".carousel-indicators"), ileft = this.ele.width(); prev.on('click', function(event) { i.ianimate(ileft); }); next.on('click', function(event) { i.ianimate(-ileft); }); span.on('click', function(event) { var e = event || window.event; var target = e.target || e.srcElement; i.ianimate(- ($(target).data('index') - i.index) * ileft); }); i.ele.hover(function() { clearInterval(i.timer); }, function() { i.automatic(); }) }
|
要处理外层div的hover事件,自然先要写一个方法automatic()用来触发动画自动播放。
automatic : function() { var next = this.ele.find(".carousel-next-btn"); this.timer = setInterval(function() { next.trigger('click') }, this.time) }
|
为了保证动画的流畅程度,在动画开始之前需要加一个判断,判断现在是否有正在进行的动画,刚开始我想到了加锁,但发现不是很好操作,于是查了一下文档,发现可以通过jq内置的is()方法来判断当前是否有动画在进行,如下。
if(!i.oUl.is(":animated")) { }
|
最后我们只需要引用此插件并调用就可以了
$('.box').sowingMap({ count : 7, time : 3000 });
|
至此,主要功能以及编写完毕,只需要在处理一下细节,一个轮播图插件就做好了。