值得分享的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 相关文章推荐
Ext JS Grid在IE6 下宽度的问题解决方法
Feb 15 Javascript
AlertBox 弹出层信息提示框效果实现步骤
Oct 11 Javascript
Ajax异步提交表单数据的说明及方法实例
Jun 22 Javascript
结合JQ1.9通过js正则判断各种浏览器版本的方法
Dec 30 Javascript
JS替换字符串中字符即替换全部而不是第一个
Jun 04 Javascript
JavaScript编程中的Promise使用大全
Jul 28 Javascript
JavaScript头像上传插件源码分享
Mar 29 Javascript
Vuejs第八篇之Vuejs组件的定义实例解析
Sep 05 Javascript
利用JS实现点击按钮后图片自动切换的简单方法
Oct 24 Javascript
微信小程序与后台PHP交互的方法实例分析
Dec 10 Javascript
小程序:授权、登录、session_key、unionId的详解
May 15 Javascript
layui实现显示数据表格、搜索和修改功能示例
Jun 03 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
在PHP3中实现SESSION的功能(一)
2006/10/09 PHP
Yii2.0使用阿里云OSS的SDK上传图片、下载、删除图片示例
2017/09/20 PHP
PHP设计模式(九)外观模式Facade实例详解【结构型】
2020/05/02 PHP
分享几种好用的PHP自定义加密函数(可逆/不可逆)
2020/09/15 PHP
js 中 document.createEvent的用法
2010/08/29 Javascript
深入理解JavaScript系列(11) 执行上下文(Execution Contexts)
2012/01/15 Javascript
防止xss和sql注入:JS特殊字符过滤正则
2013/04/18 Javascript
JQuery制作的放大效果的popup对话框(未添加任何jquery plugin)分享
2013/04/28 Javascript
客户端js判断文件类型和文件大小即限制上传大小
2013/11/20 Javascript
jquery实现图片上传前本地预览功能
2016/05/10 Javascript
AngularJS 路由详解和简单实例
2016/07/28 Javascript
超详细的JS弹出窗口代码大全
2020/04/18 Javascript
Bootstrap Navbar Component实现响应式导航
2016/10/08 Javascript
基于jquery实现五星好评
2017/11/18 jQuery
Layer弹出层动态获取数据的方法
2018/08/20 Javascript
create-react-app安装出错问题解决方法
2018/09/04 Javascript
Node.js实现用户评论社区功能(体验前后端开发的乐趣)
2019/05/09 Javascript
Vue 一键清空表单的实现方法
2020/02/07 Javascript
JavaScript获取时区实现过程解析
2020/09/24 Javascript
Django框架中方法的访问和查找
2015/07/15 Python
Python实现的拟合二元一次函数功能示例【基于scipy模块】
2018/05/15 Python
Python基于生成器迭代实现的八皇后问题示例
2018/05/23 Python
python3实现域名查询和whois查询功能
2018/06/21 Python
Python 函数绘图及函数图像微分与积分
2019/11/20 Python
Python中实现输入一个整数的案例
2020/05/03 Python
Keras load_model 导入错误的解决方式
2020/06/09 Python
世界上最大的餐具公司:Oneida
2016/12/17 全球购物
澳大利亚头发和美容产品购物网站:OZ Hair & Beauty
2020/03/27 全球购物
咖啡馆创业计划书
2014/01/26 职场文书
学员自我鉴定
2014/03/19 职场文书
大学生党员自我剖析材料
2014/10/06 职场文书
夫妻双方自愿离婚协议书
2014/10/24 职场文书
幼儿园大班教师个人总结
2015/02/05 职场文书
黄石寨导游词
2015/02/05 职场文书
工作汇报材料难写?方法都在这里了!
2019/07/01 职场文书
利用Python实现翻译HTML中的文本字符串
2022/06/21 Python