支持触屏的jQuery轮播图插件

移动轮播图我看到两类,

一款是无线天猫的m.tmall.com和携程,实现了无缝轮播。

一款是蘑菇街的,没有实现无缝轮播。

我自己重写一个,类似天猫。

1.页面代码

 1 <!DOCTYPE html>
 2 <html xmlns="http://www.w3.org/1999/xhtml" lang="UTF-8">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>基于jQuery的移动轮播图(支持触屏)</title>
 6     <meta name="viewport" content="maximum-scale=1,initial-scale=1,user-scalable=0">
 7 
 8     <style type="text/css">
 9 
10     img
11     {
12         border:0;
13         *display:inline;
14     }
15     li
16 {
17     border:0;
18     list-style-type :none;  
19 }
20 ul
21 {
22     list-style:none;
23     margin:0;
24     padding:0;
25 }
26     body{
27         overflow-x:hidden;
28         margin:0 auto;
29         padding:0;
30         width: 100%;
31         height: 100%;
32     }
33     .WSCSlideWrapper{
34         width:100%;
35         position: relative;
36         margin:0 auto;
37         //cursor:move;
38     }
39     #WSCSlideWrapperTwo{
40         width:50%;
41         position: relative;
42         margin:10px auto;
43         //cursor:move;
44     }
45     
46     </style>
47     <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/jquery-1.8.2.min.js"></script>
48 
49     <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/touchslide-1.1.js"></script>
50 </head>
51 <body>
52 
53     
54     <div class="WSCSlideWrapper" id="WSCSlideWrapper" >
55     
56         <div>
57         
58             <a  href="http://www.baidu.com"><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1.jpg" /></a>
59       <a  href="http://m.tmall.com"><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/2.jpg" /></a>
60      <a href="http://huaban.com"><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/3.jpg" /></a>            
61        </div>
62    
63     </div>
64 
65      <div class="WSCSlideWrapper" id="WSCSlideWrapperTwo" >
66         <div>
67              <a><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1 (1).jpg" /></a> 
68              <a><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1 (2).jpg" /></a>    
69              <a><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1 (3).jpg" /></a> 
70              <a><img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1 (4).jpg" /></a>             
71         </div>
72       
73     </div>
74 <script type="text/javascript">
75  
76 $(document).ready(function(){
77 
78     $('.WSCSlideWrapper').height($('.WSCSlideWrapper').width()*0.3);
79     $('#WSCSlideWrapperTwo').height($('#WSCSlideWrapperTwo').width()*1.5);
80     $('.WSCSlideWrapper').touchslide({timecontrol:3000,animatetime:300,direction:'left',navshow:true,canvassuport:true});
81 });
82 
83     
84 </script>
85 </body>
86 </html>
View Code

2.js插件代码

  1 /* 
  2 * touchslide 1.1 
  3 * Copyright (c) 2014 BowenLuo http://www.luobo.com/ 
  4 * Date: 2014-06-08
  5 * Example:$('.WSCSlideWrapper').touchslide({timecontrol:3000,animatetime:300,direction:'left',navshow:true,canvassuport:true});
  6 * Update:增加对IE9+等非Safari内核浏览器的鼠标拖动图片功能
  7 */ 
  8 (function($){ 
  9 $.fn.touchslide = function(options){ 
 10 var defaults = { 
 11     timecontrol:3000,//图片停留时间
 12     animatetime:300, //图片滑动所需时间
 13     direction:'left', //轮播方向
 14     navshow:true,//是否显示图片导航栏
 15     canvassuport:true//图片导航栏是否开启cavas绘制圆
 16 } 
 17 var options = $.extend(defaults, options); 
 18 var timecontrol = options.timecontrol||3000;
 19 var animatetime = options.animatetime||300;
 20 var direction = options.direction||'left';
 21 var navshow = options.navshow;
 22 this.each(function(){ 
 23     var slideWrapper=$(this);
 24     var slideImgWrapper = slideWrapper.children('div:first');
 25     var slideAs = slideImgWrapper.children('a');
 26     var slideImgs = slideAs.children('img');
 27     var imgcount = slideImgs.length;
 28     var imgcountrealy = slideImgs.length;
 29     var navimgs;
 30     var circlewrapper;
 31     var circles;
 32     var canvassuport = true;
 33     var circler = 0;
 34     if(imgcount>1){
 35         $((slideImgWrapper.html().split("/a>")[0]+"/a>"+slideImgWrapper.html().split("/a>")[1]+"/a>")).insertAfter(slideAs.last());
 36         if(navshow){
 37             $("<div class='navimgs'></div>").insertAfter(slideImgWrapper);
 38             navimgs = slideWrapper.children('div:last');
 39             navimgs.append("<div class='circlewrapper'></div>");
 40             circlewrapper = navimgs.children('div:first');
 41             for(var i=0;i<imgcountrealy;i++){
 42                 circlewrapper.append("<canvas class='circle' width='15' height='15'></canvas>");
 43             }
 44             circles = circlewrapper.children('canvas');
 45             var myCanvas = circles[1];
 46             if (!myCanvas.getContext) 
 47             { 
 48                 canvassuport =false;
 49             }else 
 50             { 
 51                 canvassuport =options.canvassuport;            
 52             }         
 53         }
 54     }    
 55     slideAs =  slideImgWrapper.children('a');
 56     slideImgs = slideAs.children('img');    
 57     imgcount = slideImgs.length;
 58     var slideWrapperWidth = slideWrapper.width();    
 59     var slideWrapperHeight = slideWrapper.height();
 60     slideWrapper.css({"overflow":"hidden","width":slideWrapperWidth,"height":slideWrapperHeight});
 61     slideImgWrapper.css({'position':"absolute","z-index":"1","overflow":"hidden","width":slideWrapperWidth*imgcount,"height":slideWrapperHeight});    
 62     slideAs.css({'position':"relative","overflow":"hidden","float":"left","width":slideWrapperWidth,"height":slideWrapperHeight});    
 63     slideImgs.css({'position':"relative","z-index":"1","width":slideWrapperWidth,"height":slideWrapperHeight});
 64     if(typeof(navimgs)!=='undefined'){
 65         if(navshow){
 66         navimgs.css({"position":"absolute","z-index":"3","overflow":"hidden","display":"block","width":slideWrapperWidth,"height":slideWrapperWidth*0.05,"bottom":"0"});
 67         if(slideWrapperWidth*0.05>15){
 68             navimgs.height(24);
 69         }
 70         circlewrapper.css({"position":"relative","overflow":"hidden","width":(slideWrapperWidth*0.05*imgcountrealy),"height":slideWrapperWidth*0.04,"margin":"auto"});        
 71         circles.css({'position':"relative","float":"left","max-width":15,"max-height":15,"margin-left":slideWrapperWidth*0.01});
 72         if(slideWrapperWidth*0.03>15){
 73             circler=15;
 74         }else{
 75             circler = slideWrapperWidth*0.03;
 76         }                
 77         circles.attr("width",circler);
 78         circles.attr("height",circler);
 79         canvacircle(0);        
 80         for(var i=0;i<circles.length;i++){
 81             navclick(i);
 82         }    
 83         }else{
 84             navimgs.css({"display":"none"});
 85         }
 86     }        
 87     if(imgcount<4){
 88         return;
 89     }
 90     slideImgWrapper.css({'left':-slideWrapperWidth});
 91     var st;
 92     var sts;
 93     sts = setTimeout(function(){
 94         timedCount();
 95     },timecontrol);    
 96     
 97     slideWrapper.hover(function(){
 98             stopAll();
 99         },
100         function(){
101         sts = setTimeout(function(){
102             timedCount();
103         },timecontrol);
104     });
105     mouseDrag(slideWrapper);
106     touchDrag(slideWrapper);
107         
108     function timedCount()
109     {
110         if(direction=='left'){
111             turnleft();
112         }if(direction=='right'){
113             turnright();
114         }        
115         clearTimeout(st);
116         st=setTimeout(function(){
117             timedCount();        
118         },timecontrol);
119     }
120 
121     function stopCount()
122     {
123         if (st !=""){
124             clearTimeout(st);
125         }
126     }
127     
128     function stopAll()
129     {
130         stopCount();
131         clearTimeout(sts);
132         slideImgWrapper.stop(true);
133     }
134     
135     function navclick(navnum){
136         circlewrapper.children('canvas:eq('+navnum+')').click(function(e){
137                 scideimgskip(navnum+1);
138         })
139     }
140     
141     function scideimgskip(imgnum){
142         stopAll();
143         turnleftwidth = imgnum*slideWrapperWidth;
144         slideImgWrapper.stop(true).animate({left:-turnleftwidth},animatetime,function(){
145             var imgnum = turnleftwidth/slideWrapperWidth-1;
146             if(imgnum==0){
147                 imgnum=imgcountrealy;
148             }
149             if((imgnum-imgcountrealy)==0){
150                 imgnum=0;
151             }
152             canvacircle(imgnum);
153         });    
154     }
155     
156     function canvacircle(canvanum){
157         circles.attr("width",circler);
158         circles.attr("height",circler);
159         for(var i=0;i<circles.length;i++){
160             if(canvassuport){
161                 var navCanvas=circles[i];
162                 var cxt=navCanvas.getContext("2d");
163                 if(i==canvanum){
164                     cxt.fillStyle="#0182D7";    
165                 }else{
166                     cxt.fillStyle="#ddd";    
167                 }                
168                 cxt.arc(circler*0.5,circler*0.5,circler*0.5,0,Math.PI*2,true);
169                 cxt.closePath();
170                 cxt.fill(); 
171             }else{
172                 circles.css("background","#ddd");                
173                 circlewrapper.children('canvas:eq('+canvanum+')').css("background","#0182D7");                
174             }
175             
176         }
177     }
178     
179     var turnleftwidth = slideWrapperWidth;
180     function turnleft(){
181         turnleftwidth = turnleftwidth+slideWrapperWidth;    
182         if(turnleftwidth>(imgcount-2)*slideWrapperWidth){        
183             slideImgWrapper.css({'left':0});
184             turnleftwidth = slideWrapperWidth;
185         }
186         slideImgWrapper.stop(true).animate({left:-turnleftwidth},animatetime,function(){
187             var imgnum = turnleftwidth/slideWrapperWidth-1;
188             if(imgnum==0){
189                 imgnum=imgcountrealy;
190             }
191             if((imgnum-imgcountrealy)==0){
192                 imgnum=0;
193             }
194             canvacircle(imgnum);
195         });                
196     }
197     function turnright(){     
198         turnleftwidth = turnleftwidth-slideWrapperWidth;    
199         if(turnleftwidth==0){        
200             slideImgWrapper.css({'left':-slideWrapperWidth*(imgcount-1)});
201             turnleftwidth = slideWrapperWidth*(imgcount-2);
202         }
203         slideImgWrapper.stop(true).animate({left:-turnleftwidth},animatetime,function(){
204             var imgnum = turnleftwidth/slideWrapperWidth-1;
205             if(imgnum==0){
206                 imgnum=imgcountrealy;
207             }
208             if((imgnum-imgcountrealy)==0){
209                 imgnum=0;
210             }
211             canvacircle(imgnum);
212         });                
213     }
214     
215     var distanceX=0;        
216         
217     function toLeft(){
218         if(turnleftwidth>(imgcount-3)*slideWrapperWidth){
219             slideImgWrapper.css({"left":distanceX});
220                 turnleftwidth = 0;
221             }
222         turnleft(); 
223         sts = setTimeout(function(){
224             timedCount();
225         },timecontrol);    
226     }    
227     
228     function toRight(){
229         if(turnleftwidth==slideWrapperWidth){
230             slideImgWrapper.css({'left':-slideWrapperWidth*(imgcount-1)+distanceX});
231             turnleftwidth = slideWrapperWidth*(imgcount-1);
232         }
233         turnright();    
234         sts = setTimeout(function(){
235             timedCount();
236         },timecontrol);        
237     }
238         
239     function mouseDrag($element) {
240         var eventEle = $element;
241         var stx = etx = curX = 0;
242         var MAction = false;
243         var ahrefs = [];        
244         for(var i=0;i<slideAs.length;i++){
245             if(typeof(slideImgWrapper.children('a:eq('+i+')').attr('href'))!=='undefined'){
246                 ahrefs.push(slideImgWrapper.children('a:eq('+i+')').attr('href'));
247             }        
248         }        
249         eventEle.mouseover(function(){
250             for(var i=0;i<slideAs.length;i++){
251                 if(typeof(slideImgWrapper.children('a:eq('+i+')').attr('href'))!=='undefined'){
252                     slideImgWrapper.children('a:eq('+i+')').attr('href',ahrefs[i]);
253                 }        
254             }
255         });
256         eventEle.mousemove(function(event){            
257             if(MAction){
258             var changeX = event.pageX-stx;    
259             slideImgWrapper.css({"left":-turnleftwidth+changeX});
260             distanceX = changeX;
261             }
262              event.preventDefault();
263         }).mousedown(function(event){
264             stopAll();
265             MAction = true;
266             stx = event.pageX;                
267              event.preventDefault();
268         });
269         eventEle.mouseup(function(event){
270             etx = event.pageX;
271             curX = etx-stx;
272             MAction = false;
273             if(curX>5){
274                 slideAs.attr("href","javascript:void(0)");
275                 toRight();
276             }
277             if(curX<-5){
278                 slideAs.attr("href","javascript:void(0)");
279                 toLeft();
280             }
281              event.preventDefault();
282         });
283     }
284     
285     function touchDrag($element) {
286         var gundongX = 0;
287         var gundongY = 0;
288         var moveEle = $element;
289         var stx = sty = etx = ety = curX = curY = 0;
290         
291         var ImgWidth_arr = [];
292         for (var i = 0; i < imgcount; i++) {
293             ImgWidth_arr.push(i * slideWrapperWidth);
294         }    
295         
296         moveEle.on("touchstart", function(event) { //touchstart
297             stopAll();
298             var event = event.originalEvent;
299             gundongX = 0;
300             gundongY = 0;
301             // 元素当前位置
302             etx = parseInt(getT3d(moveEle, "x"));
303             ety = parseInt(getT3d(moveEle, "y"));
304             // 手指位置
305             stx = event.touches[0].pageX;
306             sty = event.touches[0].pageY;
307         });
308         moveEle.on("touchmove", function(event) {
309             var event = event.originalEvent;
310             // 防止拖动页面
311             event.preventDefault();
312 
313             // 手指位置 减去 元素当前位置 就是 要移动的距离
314             gundongX = event.touches[0].pageX - stx;
315             gundongY = event.touches[0].pageY - sty;
316 
317             // 目标位置 就是 要移动的距离 加上 元素当前位置
318             curX = gundongX + etx;
319             curY = gundongY + ety;            
320             slideImgWrapper.css({"left":-turnleftwidth+curX});
321             distanceX = curX;
322         });
323         moveEle.on("touchend", function(event) {
324             //alert(gundongX);
325          if(Math.abs(gundongX)>5){
326             event.preventDefault();
327             
328            // 手指接触屏幕的位置
329             var oriX = etx;
330             var oriY = ety;
331             // 手指离开屏幕的位置
332             etx = curX;
333             ety = curY;
334             var slidePosition = 0;
335             for (var i = 0; i < imgcount - 1; i++) {
336                 if (Math.abs(etx) > ImgWidth_arr[i]) {
337 
338                     if (oriX > etx) {
339                         // 左滑
340                         toLeft();
341                     } else {
342                         // 右滑
343                         toRight();                
344                    }
345                 }
346             }
347          }
348             
349          });
350 
351         function getT3d(elem, ename) {
352             var elem = elem[0];
353             var str1 = elem.style.webkitTransform;
354             if (str1 == "") return "0";
355             str1 = str1.replace("translate3d(", "");
356             str1 = str1.replace(")", "");
357             var carr = str1.split(",");
358 
359             if (ename == "x") return carr[0];
360             else if (ename == "y") return carr[1];
361             else if (ename == "z") return carr[2];
362             else return "";
363         }
364     }
365 }); 
366 }; 
367 })(jQuery); 
View Code

 在线演示

3.整体思路

a.创建显示窗口,显示容器大小位置自定义;

b.创立图片容器,通过改变图片容器的位置来改变显示窗口中展示的图片。

  难点在于如何改变图片容器位置来显示需要的图片和无缝轮播。可以想象一下以前的胶带式电影,如果放映速度慢下来就跟图片轮播类似了。所以可以把图片容器看成胶带,图片就是其中的一帧图像,显示窗口就是电影屏幕。

  如果以一个显示窗口的宽为一个单位,我们把图片水平排放在图片容器中,每张图片恰好占用一个显示窗口的宽高,有多少张图片,图片容器的宽就为多少个单位宽。然后使图片向左移动一个单位的距离,就可以展示下一张图片了。为了使图片展示效果更美观,我用了jquery的动画效果animate()来改变图片容器的位置。如果设定间隔多少时间图片容器左移一个单位,达到最后一张图片时,图片容器恢复到原始位置,进行下一轮移动。这样,就可以实现图片的水平轮播了。至于无缝轮播,我采取的做法是把前两张图片复制一份,放到所有图片的最后面。首次显示第二张图片,当移动到倒数第二张图片时,在下一次移动时先改变容器的left,让第一张图片被显示出来,因为这个动作很快,看起来好像并没有进行任何操作(达到欺骗眼球的效果,呵呵),然后使用animate显示第二张图片,在视觉上实现无缝轮播。从第二张图片右移时也与此类似。这样做的主要目的是为了确保左右都有能被展示的图片,确保不会显示空白,以实现无缝切换。这里,会用到的主要知识点有:

  (1).setTimeout和clearTimeout:

  用于延迟执行、循环执行和终止循环。setTimeout延迟执行确定图片的显示时间,循环执行为了自动展示下一张图片。当需要停止轮播的时候就可以用clearTimeout来终止循环了。关于setTimeout和clearTimeout的具体用法,可以参照代码,这里就不再赘述了。

  (2).jquery选择器:

  无需多说,所有jquery动作的基础。

  (3).jquery操作css:

  为了保证使用的简单性,只需确定显示容器的样式(需确保overflow:hidden;插件中已设置),其子元素包括图片容器(这里只支持div标签slideWrapper.children('div:eq(0)'))和图片及其链接的样式都在插件中确定,包括:保证图片水平排列,大小恰好为显示窗口的大小;确定图片容器的大小恰好容下所有图片。

  (4).jquery添加元素insertAfter:

  复制前两张图片到所有图片的最后面$((slideImgWrapper.html().split("/div>")[0]+"/div>"+slideImgWrapper.html().split("/div>")[1]+"/div>")).insertAfter(slideAWra.last())。

  (5).jquery动画效果animate: 

 1 var turnleftwidth = slideWrapperWidth;
 2     function turnleft(){
 3         turnleftwidth = turnleftwidth+slideWrapperWidth;    
 4         if(turnleftwidth>(imgcount-2)*slideWrapperWidth){        
 5             slideImgWrapper.css({'left':0});
 6             turnleftwidth = slideWrapperWidth;
 7         }
 8         slideImgWrapper.stop(true).animate({left:-turnleftwidth},animatetime);                
18     }
19     function turnright(){     
20         turnleftwidth = turnleftwidth-slideWrapperWidth;    
21         if(turnleftwidth==0){        
22             slideImgWrapper.css({'left':-slideWrapperWidth*(imgcount-1)});
23             turnleftwidth = slideWrapperWidth*(imgcount-2);
24         }
25         slideImgWrapper.stop(true).animate({left:-turnleftwidth},animatetime);                
35     }

  其中imgcount为进行图片复制后图片的元素个数,slideWrapperWidth为显示窗口的宽。 turnleft()用于无缝左移,turnright()用于无缝右移。

  现在来说说触屏支持。主要是用了jquery的on方法监听touch事件。具体怎么写,无非就是设置初始触摸位置,然后监听滑动的位置,根据水平滑动的具体实时改变图片容器的left,实现拖拽的效果。滑动结束后根据结束时的位置与初始位置的差判断左滑还是右滑,再来改变图片容器的left,使图片正好居中显示。跟PC端的鼠标拖拽类似。说到鼠标拖拽,不得不喷俩句。一般拖拽元素,基本都是mousedown、mousemove、mouseup三部曲。但是碰上图片就头疼了,特别是这图片的父亲还是a标签。mousedown没问题,mousemove能一个三五步,mousemove出问题mouseup也就失效了。弄了半天,终于通过 event.preventDefault()实现了对大部分浏览器的支持。废话不多说,让大家一起纠结下。

 1 <!DOCTYPE html>
 2 <html xmlns="http://www.w3.org/1999/xhtml" lang="UTF-8">
 3 <head>
 4 <meta charset="UTF-8">
 5 <style>
 6     body{
 7         overflow-x:hidden;
 8         margin:0 auto;
 9         padding:0;
10         width: 100%;
11         height: 100%;
12     }
13     .aWrapper{            
14         position: relative;
15         margin:0 auto;
16         width:400px;
17         height:600px;
18         overflow:hidden;
19         //float:left;
20     }
21     .testa{            
22         position: relative;
23         margin:0 auto;
24         width:400px;
25         height:600px;
26         //float:left;
27     }
28     .testimg{            
29         position: absolute;
30         width:400px;
31         height:600px;
32         z-index:0;
33     }
34     .imgmask{            
35         position: relative;
36         width:400px;
37         height:600px;
38         z-index:1;
39         opacity:0.2;
40         background:#333;
41     }
42 </style>
43 </head>
44 <body>
45     <div  class="aWrapper">
46     <a href="http://www.baidu.com"  class="testa">
47         <img src="http://sandbox.runjs.cn/uploads/rs/215/auboqjjr/1 (1).jpg" class="testimg"/>
48         <div class='imgmask'></div>
49     </a>
50     </div>
51 </body>
52 </html>

  上面测试页面只有在Safari内核的浏览器(如Chrome)下才能够拖拽的,而且当图片遮罩层的position设置为absolute或者a标签设置float时都会失效,实在是搞不明白。而且要说一下可以很容易的阻止事件向上冒泡,有什么办法触发父元素事件是不要触发子元素的该事件么?还是以上面的测试页面为例,要拖拽aWrapper的div,就要设置该元素的mousedown、mousemove、mouseup事件。但是这里有一个问题,在mousedown和mouseup之后会触发click事件,导致触发子元素a标签的click而跳转页面。如果仅仅在mouseup时设置a标签的click返回false,那么这个链接就永远失效了。具体处理方法,大家还是看代码吧,反正被我搞得复杂得很,说多了都是泪。

  最后说说这个导航栏,就是下面的方方圈圈,用来显示第几张和点击跳转的。如果直接在页面上编写,问题倒还简单。在JS中就复杂多了。为了确保插件的独立性,除了对jquery的依赖外,我不想它有任何其他依赖。所以一般用图片来表示状态的方法就行不通了。我用的是html5的canvas绘制圆形。如果浏览器不支持或者担心浏览器消耗的话,可以关闭掉,直接用背景色表示,只是只能为方块块了。反正我觉得我自己弄的既不美观又不优雅,大家就当参照吧。

  

 

posted @ 2014-06-08 21:39  小小三师弟  阅读(1176)  评论(0编辑  收藏  举报