go=function(dire){ var index=active.getAttribute("index")-0, nextIndex, nextPosition; if (dire==="next") { nextIndex=(index+1)%len; nextPosition=(ele.scrollLeft+width)%(width*len); }else{ nextIndex=index===0? len-1:index-1, nextPosition=ele.scrollLeft===0?width*len:ele.scrollLeft-width; } light(nextIndex); animate.liner(ele,"scrollLeft",nextPosition); }
其中的len(图片总数)、width(图片宽度)、ele(包裹容器)也会被其他函数访问,所以也添加到作用域链末端。
len=ele.getElementsByTagName("img").length
width=parseInt(getCSS(ele.getElementsByTagName("img")[0],"width");
ele=$(eleSelec),eleSelec是包裹容器的selector,比如.carousel
4.创建箭头函数 createArrow
创建一个向左的箭头,绑定事件处理函数,用于向左移动。创建一个向右的箭头,绑定事件处理函数,用于向右移动。
createArrow=function(){ var prev=document.createElement("div"), next=document.createElement("div"); prev.appendChild(document.createTextNode("<")); next.appendChild(document.createTextNode(">")); prev.className="arrow prev"; next.className="arrow next"; container.appendChild(prev); container.appendChild(next); addClass(container,"hide"); $.add(next,"click",function(){ go("next"); }); $.add(prev,"click",function(){ go("prev"); }); }
container代表最外层容器,也会被其他函数访问,所以也添加到作用域链末端。
container=$(wrapSelec),wrapSelec是最外层容器的selector,比如.carousel-box
5.创建按钮函数 createBtn
给每个按钮添加一个index用于点亮和熄灭,给按钮组添加一个类名用于设置样式和获取它:
createBtn=function(){ var div=document.createElement("div"), btns=''; for(var i=0;i<len;i++){ btns+='<a href="#" index="'+i+'"></a>'; } div.innerHTML=btns; addClass(div,"carousel-btn"); container.appendChild(div); }
6.轮播函数
根据要求(顺时针、逆时针)判断要调用go("prev")还是go("next")。
如果要求循环,则再次调用自己。如果不循环,则在轮播一轮后停止。
所以这里需要一个变量来判断方向,一个变量来判断是否循环,一个变量来计数。
所以又有四个变量被加到作用域链末端。direction、loop、count、begin用于清除定时器。
circle=function(){ count++; if (loop||count<len) { if (direction==="forward") { go("next"); }else{ go("prev"); } } begin=setTimeout(arguments.callee,t); }
7.停止函数 stop
stop=function(){ clearTimeout(begin); }
8.初始化函数 init
如果是第一次使用轮播,则创建按钮和箭头,并给按钮绑定click事件处理程序(获取点击的按扭index点亮它,切换到相应图片),然后根据顺时针或逆时针来展示相应的图片和按钮。
所以这里又需要有一个变量加在作用域链末端,用于表示是否已经初始化。
init=function(){ createBtn(); createArrow(); $.delegateTag(wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); light(target.getAttribute("index")); animate.liner(ele,"scrollLeft",target.getAttribute("index")*width); }); $.add(container,"mouseenter",function(){ stop(); removeClass(container,"hide"); }); $.add(container,"mouseleave",function(){ addClass(container,"hide"); begin=setTimeout(circle,t); });if (direction==="forward") { light(0); }else{ light(len-1); ele.scrollLeft=width*(len-1); } haveStart=true; }
9.开始轮播函数 start
这个函数当做接口,用于控制轮播方向,间隔时间,和是否循环。计数器归零。
因为可能重复的开始轮播,所以每次开始之前都需要清除定时器。
start=function(dir,th,lo){ stop(); count=0; direction=dir; t=th*1000; loop=lo; if (!haveStart) { init(); } begin=setTimeout(circle,t); }
到这里,所有需要用到的函数已经写完了,如果把这些函数和那些需要的变量扔到一个函数里,把外层容器盒包裹容器的类名或ID传给它,这个函数返回一个包含start和stop方法的对象,这个组件就可以使用了。
但是有一个问题,这个函数只有一个,也就是说,一个页面只能有一个轮播实例。所以,如果想要一个页面能有两个轮播实例都用这个组件的话,就不能把它们扔到一个函数里。那么就只能放到对象里。每个对象有自己的变量,他们共用一组方法。
那么,这些变量就不能直接访问了,需要通过对象的属性访问,即this。
这时候就会出现问题,this是会指向调用它的那个环境,所以当那些变量在事件处理程序中,或是在定时器中被访问的时候,就不能用this,而是要创建一个闭包。
即,在能获取到this时,将this赋值给一个变量,然后在事件处理程序或是定时器中访问这个变量,就会获取到正确的对象。
以init函数为例来改装: