This article introduces the JavaScript implementation of the image carousel component. Without further ado, let’s just look at the following:
Effect:
Automatically loop through the images, and there is a button below to switch to the corresponding image.
Add an animation to switch pictures.
When the mouse is parked on the picture, the carousel stops, and two arrows on the left and right appear. Click to switch pictures.
When the mouse moves away from the picture area, the carousel will continue from the current position.
Provides an interface to set the carousel direction, whether to loop, and the interval.
Requirements for HTML and CSS:
<div class="carousel-box"> <div class="carousel"> <ul class="clearfix" > <li><img src="img/carousel01.jpg" alt=""></li> <li><img src="img/carousel02.jpg" alt=""></li> <li><img src="img/carousel03.jpg" alt=""></li> <li><img src="img/carousel04.jpg" alt=""></li> <li><img src="img/carousel05.jpg" alt=""></li> <li><img src="img/carousel06.jpg" alt=""></li> </ul> </div> </div>
must be two boxes nested, the innermost box needs to have a ul, and the image needs to be included in the li.
You can change the class name and replace the corresponding class name in the css file. Just pass in the correct DOM element when configuring the component.
There is no limit to the width and number of images, just change the values in the css file.
/*需要更改的值*/ .carousel img{ width: 600px; height: 400px; } .carousel, .carousel-box { width: 600px; /*单张图片宽度*/ height: 400px; /*单张图片高度*/ } .carousel ul{ width: 3600px; /*单张图片宽度x图片数量*/ }
Principle:
Arrange all images horizontally, and set overflow:hidden on the outermost container and package container. The outermost container is used for positioning buttons and arrows. Use the scrollLeft property of the wrapper container to control which image is displayed.
Idea:
To implement these functions, there should be the following methods:
1. Image switching function. Accepts a parameter indicating the scroll direction. Call the easing function to switch pictures. Call the toggle button icon function to light up the corresponding button.
2. Easing function.
3. Light up the button function.
4. Initialization function. Used to bind events, create buttons and arrows, and initialize initial positions.
5. Create arrow functions.
6. Create button function.
7. Start the carousel function.
8. Carousel function.
9. Stop function. Used to stop the carousel.
There are also some public methods: $(): Select DOM elements. addClass(ele,"className"): Add a class name to the element. removeClass(ele,"className") removes the class name of an element. $.add(ele,"type",fun): Bind an event to a DOM node. getCSS(ele,"prop"): Get the value of the corresponding attribute of the element. $.delegateTag("selector","tagName","type",fun): event proxy.
Implementation:
Suppose there are 6 pictures, each picture has a width of 600px. It is completed according to the independence of functions:
1. Easing function liner
The function of the easing function is to change the attribute value of the target element bit by bit until the target value is reached. The element using it may be a horizontally rotated image, a vertically rotated image, or a small box that wants to reach from the left end of the page to the right end of the page. So it should receive four parameters (target element, attribute value to be changed, target value, number of moves).
liner=function(ele,prop,next,num){ var speed=(next-ele[prop])/num, i=0; (function(){ ele[prop]+=speed; i++; if (i<num) { setTimeout(arguments.callee,30); } })(); },
2. Light button function light
Lighting a button is essentially adding an active class to the button, and turning off the button is to remove the active class from the button.
So how do you know which button the current button is?
The easiest way is to obtain it directly, so you can add an index attribute to each button. When you need to light the button, just pass the index of the button to be lit to this function.
So how do you know which button to turn off?
The simplest method is to obtain it directly, so you can add a variable active at the end of the scope chain, remember the currently lit button, and this function can directly turn it off.
light=function(index){ removeClass(active,"active"); active=$(this.wrapSelec+" "+"[index="+index+"]"); addClass(active,"active"); }
3. Image switching function go
needs to calculate the next scrollLeft value:
If it is moving to the left, scrollLeft should be -600, if it is already 0, switch to 3000. So it is ele.scrollLeft= ==0?width*(len-1):ele.scrollLeft-width;
If you move to the right, scrollLeft should be +600, that is, 0——>600,600——>1200,...,3000 ——>0. Here you can use judgment as above, or you can use a formula next=(cur+distance)%(distance*num). That is, (ele.scrollLeft+width)%(width*len)
You need to get the index of the next button to be lit:
The same idea as calculating scrollLeft, move to the left: index===0? len- 1:index-1; Move to the right: (index+1)%len
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); }
The len (total number of pictures), width (picture width), and ele (wrapped container) will also be accessed by other functions, so they are also added to The end of the scope chain.
len=ele.getElementsByTagName("img").length
width=parseInt(getCSS(ele.getElementsByTagName("img")[0],"width");
ele=$(eleSelec),eleSelec is Wrap the selector of the container, such as .carousel
4. Create an arrow function createArrow
Create a left arrow and bind the event handler for moving left. Create a right arrow and bind the event handler. , used to move to the right.
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 represents the outermost container and will also be accessed by other functions, so it is also added to the end of the scope chain.
container=$(wrapSelec), wrapSelec is the selector of the outermost container. , such as .carousel-box
5. Create button function 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函数为例来改装:
carouselProto.init=function(){ var that=this; this.createBtn(); this.createArrow(); $.delegateTag(this.wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); that.light(target.getAttribute("index")); animate.liner(that.ele,"scrollLeft",target.getAttribute("index")*that.width); }); $.add(this.container,"mouseenter",function(){ that.stop(); removeClass(that.container,"hide"); }); $.add(this.container,"mouseleave",function(){ addClass(that.container,"hide"); that.begin=setTimeout(function(){ that.circle(); },that.t); });if (this.direction==="forward") { this.light(0); }else{ this.light(this.len-1); this.ele.scrollLeft=this.width*(this.len-1); } this.haveStart=true; };
这样改装完之后,就可以创建实例了,每个实例都会有自己的属性用于记录状态,他们都共用原型中的方法。
如果采用原型继承的方式的话,可以创建一个对象作为实例的原型对象,然后创建一个函数来生产实例:
var carouselProto={}; //把上面那些方法给这个对象 carouselProto.light=... carouselProto.go=... carouselProto.stop=... //创建实例对象函数 var carousel=function(eleSelec,wrapSelec){ var that=Object.create(carouselProto); that.wrapSelec=wrapSelec; that.ele=$(eleSelec); that.container=$(wrapSelec); that.len=that.ele.getElementsByTagName("img").length; that.width=parseInt(getCSS(that.ele.getElementsByTagName("img")[0],"width")); return that; } //创建实例,使用组件 var carousel1=carousel(".carousel",".carousel-box"); carousel1.start("forward",3,true); var carousel2=carousel(".carousel2",".carousel-box2"); carousel2.start("backward",2,true);
性能优化:
1.当点击的按钮刚好是当前被点亮的按钮时,依然会调用一次light和animate.liner。所以可以添加一个判断语句,如果点击的按钮刚好是正确的,就不要执行下面的了。
$.delegateTag(this.wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); var index=target.getAttribute("index"); if (index===that.active.getAttribute("index")) { return } that.light(index); animate.liner(that.ele,"scrollLeft",target.getAttribute("index")*that.width); });
2.当图片切换的时候,缓动动画正在执行。如果在缓动动画还没执行完时就点击按钮或者箭头,就会进入下一次动画,于是就会出现混乱,图片错位。性能也会受到影响。为了防止这种情况发生,可以使用一个变量,用于记录缓动动画是否正在执行,没有执行的话点击按钮或箭头才会执行函数。
liner=function(ele,prop,next){ var speed=(next-ele[prop])/10, i=0; ele.animating=true; (function(){ ele[prop]+=speed; i++; if (i<10) { setTimeout(arguments.callee,60); }else{ ele.animating=false; } })(); } if (!this.ele.animating) { this.light(nextIndex); animate.liner(this.ele,"scrollLeft",nextPosition); }