值得分享的JavaScript实现图片轮播组件


Posted in Javascript onNovember 21, 2016

本文实例为大家分享了JavaScript实现图片轮播组件的使用方法,供大家参考,具体内容如下

效果:

自动循环播放图片,下方有按钮可以切换到对应图片。
添加一个动画来实现图片切换。
鼠标停在图片上时,轮播停止,出现左右两个箭头,点击可以切换图片。
鼠标移开图片区域时,从当前位置继续轮播。
提供一个接口,可以设置轮播方向,是否循环,间隔时间。
点击查看demo

对HTML、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>

*必须是两个盒子嵌套,最里面的盒子需要有一个ul,图片需要被包含在li里。
*可以更改类名,同时将css文件中的相应类名替换即可。配置组件时传入正确的DOM元素即可。
*不限制图片宽度和数量,在css文件中更改数值即可。

/*需要更改的值*/
.carousel img{ 
 width: 600px;
 height: 400px;
}
.carousel,
.carousel-box {
 width: 600px; /*单张图片宽度*/
 height: 400px; /*单张图片高度*/
}
.carousel ul{
 width: 3600px; /*单张图片宽度x图片数量*/
}

原理:

将所有图片横向排列,最外层容器和包裹容器设置overflow:hidden。最外层容器用于按钮和箭头的定位。利用包裹容器的scrollLeft属性控制显示哪张图片。

思路:

想要实现这些功能,应该有以下一些方法:

1.图片切换函数。接受一个参数,表示滚动方向。调用缓动函数切换图片。调用切换按钮图标函数点亮相应的按钮。

2.缓动函数。

3.点亮按钮函数。

4.初始化函数。用于绑定事件,创建按钮和箭头,初始化最初位置。

5.创建箭头函数。

6.创建按钮函数。

7.开始轮播函数。

8.轮播函数。

9.停止函数。用于停止轮播。

还有一些公用方法

$():选择DOM元素。

addClass(ele,"className"):给元素添加类名。

removeClass(ele,"className"):移除元素的类名。

$.add(ele,"type",fun):给一个DOM节点绑定事件。

getCSS(ele,"prop"):获取元素相应属性的值。

$.delegateTag("selector","tagName","type",fun):事件代理。

实现:

假设有6张图片,每张图片宽度为600px。按照功能的独立性来完成:

1.缓动函数  liner
缓动函数的作用是一点一点的改变目标元素的属性值,直到达到目标值。使用它的元素可能是水平轮播的图片,也可能是垂直轮播的图片,也可能是一个想从页面左端到达页面右端的小盒子。所以它应该接收四个参数(目标元素,要改变的属性值,目标值,移动次数)。

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
点亮按钮本质上就是给按钮添加一个active类,熄灭按钮就是给按钮移除active类。

那么如何知道当前按钮是哪一个呢?

最简单的方法是直接获取,所以可以给每个按钮添加一个index属性,当需要点亮按钮时,将要点亮的按钮的index传给这个函数即可。

那么如何知道要熄灭的按钮是哪一个呢?

最简单的方法也是直接获取,所以可以在作用域链末端添加一个变量active,记住当前亮着的按钮,这个函数直接将他熄灭就可以了。

light=function(index){
 removeClass(active,"active");
 active=$(this.wrapSelec+" "+"[index="+index+"]");
 addClass(active,"active");
}

3.图片切换函数  go
需要计算出下一个scrollLeft的值:

如果是向左移动的话,scrollLeft应该-600,如果已经是0,就切换为3000.所以是ele.scrollLeft===0?width*(len-1):ele.scrollLeft-width;

如果是向右移动的话,scrollLeft应该+600,即0——>600,600——>1200,...,3000——>0。这里可以像上面那样用判断,也可以用一个公式next=(cur+distance)%(distance*num)。即(ele.scrollLeft+width)%(width*len)

需要获得下一个要被点亮的按钮的index:

和计算scrollLeft的思路一样,往左移动:index===0? len-1:index-1; 往右移动:(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); 
}

其中的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函数为例来改装:

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);
}

点击查看源码 

参考资源:

慕课网——焦点图轮播特效
《JavaScript:The Good Parts》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Jquery.LazyLoad.js修正版下载,实现图片延迟加载插件
Mar 12 Javascript
jquery查找tr td 示例模拟
May 08 Javascript
jQuery简单实现QQ空间点赞已经取消点赞
Apr 02 Javascript
jfreechart插件将数据展示成饼状图、柱状图和折线图
Apr 13 Javascript
JavaScript获取数组最小值和最大值的方法
Jun 09 Javascript
微信小程序 教程之wxapp 视图容器 view
Oct 19 Javascript
jquery平滑滚动到顶部插件使用详解
May 08 jQuery
ES6中的Promise代码详解
Oct 09 Javascript
浅谈Angularjs中不同类型的双向数据绑定
Jul 16 Javascript
JavaScript数据结构与算法之检索算法实例分析【顺序查找、最大最小值、自组织查询】
Feb 22 Javascript
微信小程序实现单列下拉菜单效果
Apr 25 Javascript
使用vue for时为什么要key【推荐】
Jul 11 Javascript
使用Ajax生成的Excel文件并下载的实例
Nov 21 #Javascript
javascript入门之数组[新手必看]
Nov 21 #Javascript
jQuery UI插件实现百度提词器效果
Nov 21 #Javascript
javascript中Number的方法小结
Nov 21 #Javascript
Bootstrap中定制LESS-颜色及导航条(推荐)
Nov 21 #Javascript
js实现HashTable(哈希表)的实例分析
Nov 21 #Javascript
详解js中call与apply关键字的作用
Nov 21 #Javascript
You might like
实现php加速的eAccelerator dll支持文件打包下载
2007/09/30 PHP
php引用地址改变变量值的问题
2012/03/23 PHP
PHP爆绝对路径方法收集整理
2012/09/17 PHP
解析PHP中一些可能会被忽略的问题
2013/06/21 PHP
PHP中strpos、strstr和stripos、stristr函数分析
2016/06/11 PHP
通过源码解析Laravel的依赖注入
2018/01/22 PHP
Javascript实例教程(19) 使用HoTMetal(4)
2006/12/23 Javascript
Maps Javascript
2007/01/22 Javascript
实现超用户体验 table排序javascript实现代码
2009/06/22 Javascript
JS request函数 用来获取url参数
2010/05/17 Javascript
jQuery获取浏览器中的分辨率实现代码
2013/04/23 Javascript
Javascript中的delete操作符详细介绍
2014/06/06 Javascript
jQuery插件slick实现响应式移动端幻灯片图片切换特效
2015/04/12 Javascript
NodeJs——入门必看攻略
2016/06/27 NodeJs
jQuery事件委托之Safari
2016/07/05 Javascript
使用原生JS实现火锅点餐小程序(面向对象思想)
2019/12/10 Javascript
vue实现评价星星功能
2020/06/30 Javascript
vue在响应头response中获取自定义headers操作
2020/07/24 Javascript
ant design vue导航菜单与路由配置操作
2020/10/28 Javascript
Python实现竖排打印传单手机号码易撕条
2015/03/16 Python
RC4文件加密的python实现方法
2015/06/30 Python
python基础教程之五种数据类型详解
2017/01/12 Python
python实现媒体播放器功能
2018/02/11 Python
python使用正则表达式来获取文件名的前缀方法
2018/10/21 Python
在python image 中实现安装中文字体
2020/05/16 Python
amazeui时间组件的实现示例
2020/08/18 HTML / CSS
函授毕业个人自我评价
2014/02/20 职场文书
母校寄语大全
2014/04/10 职场文书
合同协议书格式
2014/04/18 职场文书
激励员工的口号
2014/06/16 职场文书
学生安全责任书范本
2014/07/24 职场文书
社区党员群众路线教育实践活动心得体会
2014/11/03 职场文书
个人德育工作总结
2015/03/05 职场文书
导游词之上海杜莎夫人蜡像馆
2019/11/22 职场文书
Android Rxjava3 使用场景详解
2022/04/07 Java/Android
CSS的calc函数用法小结
2022/06/25 HTML / CSS