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实现图片轮播组件的更多相关文章
- 一分钟搞定AlloyTouch图片轮播组件
轮播图也涉及到触摸和触摸反馈,同时,AlloyTouch可以把惯性运动打开或者关闭,并且设置min和max为运动区域,超出会自动回弹. 除了一般的竖向滚动,AlloyTouch也可以支持横向滚动,甚至 ...
- Angular2组件与指令的小实践——实现一个图片轮播组件
如果说模块系统是Angular2的灵魂,那其组件体系就是其躯体,在模块的支持下渲染出所有用户直接看得见的东西,一个项目最表层的东西就是组件呈现的视图.而除了直接看的见的躯体之外,一个完整的" ...
- Omi-touch实战 移动端图片轮播组件的封装
pc端的轮播,移动端的轮播都很常见.一年前,我还为手机端没有左滑,右滑事件从而封装了一个swipe库,可以自定义超过多少滑动时间就不触发,也可以设置滑动多少距离才触发,这一个功能的代码就达到400多行 ...
- Vue学习—Vue写一个图片轮播组件
1.先看效果: 熟悉的图片轮播,只要是个网站,百分之90以上会有个图片轮播.我认为使用图片轮播. 第一可以给人以一种美观的感受,而不会显得网站那么呆板, 第二可以增加显示内容,同样的区域可以显示更多内 ...
- javascript 实现图片轮播和点击切换功能
图片轮播是网页上一个比较常见的功能,下面我们来实现他吧 原理很简单: 1:固定的区域,所有的图片重叠,一次只能显示一张图片 2:通过改变图片的zIndex属性改变显示的图片,就可以达到切换的效果了 & ...
- 如何将angular-ui的图片轮播组件封装成一个指令
在项目开发中我们经常会遇到图片轮播的功能点: 如果我们开发人员自己原生手写,将会花费很多的时间,最终得不偿失. 接下来就详细说说如何使用angular-ui发热图片轮播模块,并且将它写成一个指令(便于 ...
- 如何将angular-ui-bootstrap的图片轮播组件封装成一个指令
在项目开发中我们经常会遇到图片轮播的功能点: 如果我们开发人员自己原生手写,将会花费很多的时间,最终得不偿失. 接下来就详细说说如何使用angular-ui发热图片轮播模块,并且将它写成一个指令(便于 ...
- 原生Javascript实现图片轮播效果
首先引入js运动框架 function getStyle(obj,name){ if(obj.currentStyle){ return obj.currentStyle[name]; } else{ ...
- EUI Scroller实现图片轮播 组件 ItemScroller
一 自定义组件如下 /** * 文 件 名:ItemScroll.ts * 功 能: 滚动组件 * 内 容: 自定义组件,支持多张图片水平(垂直)切换滚动 * * Example: * 1. 从自定义 ...
随机推荐
- nw.js桌面软件开发系列 第0.1节 HTML5和桌面软件开发的碰撞
第0.1节 HTML5和桌面软件开发的碰撞 当我们谈论桌面软件开发技术的时候,你会想到什么?如果不对技术本身进行更为深入的探讨,在我的世界里,有这么多技术概念可以被罗列出来(请原谅我本质上是一个Win ...
- 2D、3D形变
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #a5b2b9 } span.Apple-tab-span { ...
- Python-Jenkins API使用 —— 在后端代码中操控Jenkins
最近在工作中需要用到在后台代码中触发Jenkins任务的构建,于是想到Jenkins是否有一些已经封装好的API类库提供,用于处理跟Jenkins相关的操作.下面就简单介绍下我的发现. Linux C ...
- dubbox微服务实例及引发的“血案”
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...
- Linux学习之探索文件系统
Linux,一起学习进步- ls With it, we can see directory contents and determine a variety of important file ...
- Maven 代理设置
在maven的安装目录下 %MAVEN_HOME%/conf/setting.xml 中进行设置 <proxies> <!-- proxy | Specificatio ...
- 海康网络摄像机YV12转换为BGR,由opencv Mat显示 (转)
我使用的是海康DS-2CD852MF-E, 200万,网络摄像机,已经比较老了,不过SDK在海康官网下载的,开发流程都差不多. 海康摄像机回调解码后的视频数据格式为YV12,顺便说一下YV12的数据格 ...
- webpack学习总结
前言 在还未接触webpack,就有几个疑问: 1. webpack本质上是什么? 2. 跟异步模块加载有关系吗? 3. 可否生成多个文件,一定是一个? 4. 被引用的文件有其他异步加载模块怎么办? ...
- BPM配置故事之案例11-操作外部数据源
小明:可以获取ERP数据了-- 老李:哦,这么快?小伙子,我非常看好你,来来,别急着走,再陪我聊会-- 小明:--您老人家不是又要改流程吧? 老李:没有没有,哎嘿嘿嘿,我们这不都是为公司效率着想嘛,这 ...
- ios label 自动计算行高详解
在OC当中自动计算行高主要调用系统的 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #ffffff } span ...