移动轮播图我看到两类,
一款是无线天猫的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绘制圆形。如果浏览器不支持或者担心浏览器消耗的话,可以关闭掉,直接用背景色表示,只是只能为方块块了。反正我觉得我自己弄的既不美观又不优雅,大家就当参照吧。