zoukankan      html  css  js  c++  java
  • 原生js实现响应式轮播图,支持电脑端点击切图,手机端滑动切图

    轮播图的实现原理并不难,但是步骤有些繁琐。最近练习了一个轮播图,大部分是跟着网上的教程写的,然后自己做了一点兼容ie8的修改,加了点击切换图片的特效和手机端的滑动特效,让这个轮播图可以在响应式的网站中使用,同时兼容pc端和触屏端。

    轮播图的样式也分很多种,淡入淡出的轮播图很容易实现,只需要把图片全都叠在一起,让相应的图片轮流显示就行了,但是滚动能的轮播图就要复杂很多。这里介绍的是滚动的轮播图:

    原理:

    实现的原理就是将所有的图片横向的排列起来,排成一个长方形,让这个长方形的总体不断平移,从而使图片轮流显示在div中,然后将div之外的隐藏起来就可以了

    下面是详细步骤:

    第一步,基本骨架

    1)将要轮播的所有的图片都放到一个无序列表中

    <div id="outer">
                <ul id="imgList">
                    <li><img src="img/1.jpg"/></li>
                    <li><img src="img/2.jpg"/></li>
                    <li><img src="img/3.jpg"/></li>
                    <li><img src="img/4.jpg"/></li>
                    <li><img src="img/5.jpg"/></li>
                </ul>
            </div>

    2)所有的li标签左浮动,只要ul的宽度够大,所有的图片就会横向的排列起来。

    #imgList>li{
                    float: left;
                }

    3)让图片的宽高等于div的宽高,这样做不但能够提高开发的效率,还更便于网站的维护,即使图片源的尺寸都是不一样的,也能正常显示:

    //getCompteredStyle方法和current方法作用相同,都是获取dom对象的属性,前者不兼容ie8,后者不兼容firefox,这样写可以解决兼容性的问题。
    function
    getStyle(obj, name) { if(window.getComputedStyle) { return getComputedStyle(obj, null)[name]; } else { return obj.currentStyle[name]; } } //-- 获取outer的宽度 var getOuterWidth = getStyle(outer,"width");
    //去掉获取到的宽度值的px,使成为number类型的变量,方便计算
    var widthObject = getOuterWidth.match(/d*/); var width = widthObject[0];

    第二步,添加几个导航点和两个按钮

    <div id="navContainer">
              <!--我们就是导航点 --> <a href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> </div>
           <!--我们是左右切换按钮--> <a href="javascript:;" id="picLbtn"><img src="img/pic/picLbtn.png"></a> <a href="javascript:;" id="picRbtn"><img src="img/pic/picRbtn.png"></a>

     1)这里的圆点使用a标签来做,a标签为内联元素,是不能调整大小的。这里使用float:left给他设置为右浮动之后他就会转化为块级元素。当然,直接使用display:block也是可以的:

    #navContainer>a{
                    z-index: 5;
                    float: left;
                     15px;
                    height: 15px;
                    background-color: red;
                    margin: 0 5px;
                    opacity: 0.5;
                    filter: alpha(opacity=50);
                    border-radius: 100%;
                }

    这里把a标签的数量写固定了,如果上面放置了十张图片轮播,下面就要写十个导航点。

    可以根据图片的数量动态的设置a标签的数量,不过这样写意义不大:

    获取所有的imgList中所有的li标签放在对象x中,
    for(i=0,i<x.length-1){
    x.appendchild(a)//每次循环向数组中添加一个
    }
    a.href=javascript:;

     2)添加两张图片作为向左向右切换的按钮,这种图片很多网站都有,如果不会ps的话随便找个带轮播图的网站占下来就可以用

    3)导航点和轮播图的位置要继承div的位置,并且要动态的设置:

    //(大div的宽度 - 导航区域的宽度)/ 2 = 导航区域的左边距
    navContainer.style.left = (outer.offsetWidth - navContainer.offsetWidth)/2 + "px";
    //(大div的高度 - 按钮区域的高度)/ 2 = 按钮区域的上边距
    var tempBtnTop = (outer.offsetHeight - picLbtn.offsetHeight)/2 + "px"; picLbtn.style.top = tempBtnTop; picRbtn.style.top = tempBtnTop;

    这样,这样不管大div改成多大,都不需要修改图片和导航点的定位,这个思想后面会多次体现。

    第三步,自动切换图片

    1)这里需要写一个move函数,作用是设置ul移动的平移效果。调用该函数时需要传递四个参数:要执行动画的对象,执行对象的属性,执行对象的目标,速度,回调函数

    function move(obj, attr, target, speed, callback){
                        //关闭上一个定时器
                        clearInterval(obj.timer);
                        //获取元素目前的位置
                        var current = parseInt(getStyle(obj, attr));
                        //判断速度的正负值
                        //如果从0 向 800移动,则speed为正
                        //如果从800向0移动,则speed为负
                        if(current > target) {
                            //此时速度应为负值
                            speed = -speed;
                        }
                        //开启一个定时器,用来执行动画效果
                        //向执行动画的对象中添加一个timer属性,用来保存它自己的定时器的标识
                        obj.timer = setInterval(function(){
                            //获取box1的原来的left值
                            var oldValue = parseInt(getStyle(obj, attr));
                            //在旧值的基础上增加
                            var newValue = oldValue + speed;
                            //判断newValue是大于800
                            //从800 向 0移动
                            //向左移动时,需要判断newValue是否小于target
                            //向右移动时,需要判断newValue是否大于target
                            if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)) {
                                newValue = target;
                            }
                            //将新值设置给box1
                            obj.style[attr] = newValue + "px";
                            //当元素移动到0px时,使其停止执行动画
                            if(newValue == target) {
                                //达到目标,关闭定时器
                                clearInterval(obj.timer);
                                //动画执行完毕,调用回调函数
                                callback && callback();
                            }
                        }, 30);
                    }

    2)自动轮播效果主要由autoChange函数实现,index变量再0到(imgArr.length - 1)之间不断循环,就实现了图片从一直循环展示

    将偏移量设置为(-width*index),index函数每次都是累加的,这样每次的偏移量都是width的值,也就是图片的宽度

    这段代码方放在setInterval函数中,setInterval函数的时间参数就是轮播图的轮播速度

    function autoChange(){
                        //--这个定时器可以控制图片停留的时间
                        //--每隔三秒执行一次,不断的被执行图片就不断切换
                        timer = setInterval(function(){
                            index++;
                            //--index对长度取余,就可以让index一直在范围内循环
                            index %= imgArr.length;
                            //--这个move实现自动切换的平移效果
                            move(imgList , "left" , -width*index , 20 , function(){
                                //--执行move函数时调用set方法,将图片颜色改变,并且首末过渡
                                setA();
                                setRed();
                            });
                        },3000);
                    }

    3)从最后一张图片到第一张需要一个过渡效果,就是前面原理图中的首尾相连

    首先要在最后一张的图片中再添加第一张图片

    <ul id="imgList">
                    <li><img src="img/pic/1.jpg"/></li>
                    <li><img src="img/pic/2.jpg"/></li>
                    <li><img src="img/pic/3.jpg"/></li>
                    <li><img src="img/pic/4.jpg"/></li>
                    <li><img src="img/pic/5.jpg"/></li>
                    <li><img src="img/pic/1.jpg"/></li>
                </ul>

    然后加一个判断,当执行的最后一张时,让他index立即变成0,当前显示的图片也会由最后一张变成第一张,而且因为最后一张和第一张时完全相同的,所以这个改变也是看不出来的,但是因为索引改变了,其他的也就跟着变成第一张了,再次自动循环的时候,就会自动变成第二张了。

    function setA(){
                        //判断当前索引是否是最后一张图片
                        if(index >= imgArr.length - 1){
                            //--如果当前图片是最后一张,将该图片立即变为第一张
                            index = 0;
                            imgList.style.left = 0;
                        }
                    }

    4)改变下面导航点的颜色,循环到哪个图片,对应的导航点就会变成黑色,其他的则为红色:

    function setRed(){
                        for(var i=0 ; i<allA.length ; i++){
                            //--去掉内联样式里的颜色,显示样式表里的红色
                            //--如果这里不这么写的话,他就会每次遍历到某个图片,就会把他的导航点变成黑色,不会变回来
                            //--这么写每次调用都会先把大家都变成红色,再把需要的变成黑色
                            allA[i].style.backgroundColor = "";
                        }
                        allA[index].style.backgroundColor = "black";
                    }

    第四步,点击导航点切换图片

    1)写一个for循环,0到长度减1赋值给allA对象的num属性,这样每个导航点都对应着一个下标:

    2)当点击导航点时,触发事件,将index设置为当前的num值,在执行move函数,他的偏移量就是改变后的-width * index,图片就会跳转到当前导航点对应的图片,具体代码如下:

    var allA = document.querySelectorAll("#navContainer>a");
    for
    (var i=0; i<allA.length ; i++){ //为每一个超链接都添加一个num属性 allA[i].num = i; allA[i].onclick = function(){ //--在点击时,要关闭自动切换的定时器 clearInterval(timer); //--获取点击超链接的索引,并将其设置为index index = this.num; //--setA只控制导航点时的效果 setRed(); //这个move是实现点击切换的平移效果,每次调用只会执行一次 //--四个参数:要执行动画的对象,执行对象的属性,执行对象的目标,速度,回调函数 move(imgList , "left" , -width*index , 20 , function(){ //开启被上面clearInterval关闭的自动切换程序 autoChange(); }); }

     在执行单击触发函数时要用clearInterval把自动切换图片的定时器timer关掉,不然单击切换图片和自动切换图片同时执行网页会非常混乱。

    同时也要调用setA函数,要将当前点击的图片设置为导航点变为黑色。

    第五步,点击左右按钮切换图片

    1)关闭timer定时器

    2)左边的点击按钮是显示当前图片左边的图片,右边的点击按钮是显示当前图片右边的图片。因此要进行如下判断:

        如果当前是第一张:左边的点击按钮不工作

        如果是当前是最后一张:右边的点击按钮不工作(为了方便理解,和代码中的是反着写的)

    3)完成判断后,调用setA函数设置导航点的颜色,然后开启正常的循环:

    picLbtn.onclick = function(){
                        clearInterval(timer);
                   if (index != 0){
                            index = index-1;
                        }
                        setRed();
                        move(imgList , "left" , -width*index , 20 , function(){
                            autoChange();
                        });
                    }
    picRbtn.onclick
    = function(){ clearInterval(timer); if (index != allA.length - 1) { index = index+1; } setRed(); move(imgList , "left" , -width*index , 20 , function(){ autoChange(); }); } }

    第六步,滑动屏幕切换图片

    这个滑动效果只有在手机端电脑端或者打开浏览器的toggle device toobar模式才能用,光用鼠标拖拽是没有用的!!

    1)定义4个变量,用来存放一些数值:

    var startX = 0; //记录起始  刚刚触摸的点的位置 x的坐标
    var moveX = 0;  //滑动的时候x的位置
    var distanceX = 0;  //滑动的距离
    var isMove = false;//是否滑动了

    2)定义手指触摸屏幕触发函数:

    在手指触摸到屏幕时,将触摸点的横坐标保存到startX变量中:

    outer.addEventListener('touchstart', function(e){
          clearInterval(timer);
          startX = e.touches[0].clientX;  //--触摸点的横坐标
    });

    3)定义手指滑动触发函数:

    touchmove事件是手指触摸在屏幕上就一直触发的,因此distanceX函数最终保存的值将是手指离开屏幕时的值,用这个值减去startX就是滑动的距离:

    这里传递给move函数的偏移量值是(-width*index + distanceX),因此ul偏移的宽度是distanceX,就实现了图片跟随手指滑动的效果:

    outer.addEventListener('touchmove',function(e){e
         moveX = e.touches[0].clientX;//--获取手指当前接触屏幕位置的横坐标
         distanceX = moveX - startX; //--移动的距离=现在-初始
         isMove = true;
         move(imgList , "left" , -width*index+distanceX , 20 , function(){});
    });

     4)定义手指离开屏幕触发函数

    用div的宽度除以3(记作x),比较用刚才获得的滑动的距离(记作y)。

    如果y > x,认为滑动有效

       如果往左滑就是切右边的图

       如果往右滑就是切左边的图

    如果y < x,认为滑动的距离太短了,滑动无效,吸附回去

    当前如果是第一张,还往右滑,或者是最后一张还能往左滑,同样认为滑动无效,吸附回去

    outer.addEventListener('touchend', function(){
                    //--滑动超过 1/3才可以切换图片,否则即为无效,则吸附回去
                    //math.abs取绝对值
                        if(isMove && Math.abs(distanceX) > width/3){
                            if(distanceX > 0 && index != 0){  //上一张
                                index = index - 1;
                            }
                            else if(distanceX < 0 && index != imgArr.length - 2){   //下一张
                                index = index + 1;
                            }
                            move(imgList , "left" , -width*index, 20 , function(){});
                        }
                        else if(isMove && Math.abs(distanceX) < width/3){
                            move(imgList , "left" , -width*index, 20 , function(){});
                        }
                        setRed();
                        autoChange();
                    });

    最后整理之后的代码就是这样:

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		
    		<style type="text/css">
    			*{
    				margin: 0;
    				padding: 0;
    			}
    			#outer{
    				 500px;
    				height: 333px;
    				margin: 50px auto;
    				position: relative;
    				overflow: hidden;
    			}
    			#imgList{
    				list-style: none;
    				position: absolute;
    				left: 0px;
    			}
    			#imgList>li{
    				float: left;
    			}
    			#imgList>li>img{
    				 500px;
    				height: 333px;
    			}
    			#navContainer{
    				position: absolute;
    				bottom: 15px;
    			}
    			
    			#navContainer>a{
    				z-index: 5;
    				float: left;
    				 15px;
    				height: 15px;
    				background-color: red;
    				margin: 0 5px;
    				opacity: 0.5;
    				filter: alpha(opacity=50);
    				border-radius: 100%;
    			}
    			#navContainer>a:hover{
    				background-color: black;
    			}
    			#picLbtn, #picRbtn{
    				position: absolute;
    			}
    			#picLbtn{
    				left: 8px;
    			}
    			#picRbtn{
    				right: 8px;
    			}
    		</style>
    		<script type="text/javascript">
    			window.onload = function(){
    				var imgList = document.getElementById("imgList");
    				var navContainer = document.getElementById("navContainer");
    				var outer = document.getElementById("outer");
    				var picLbtn = document.getElementById("picLbtn");
    				var picRbtn = document.getElementById("picRbtn");
    				var imgArr = document.querySelectorAll("#imgList>li>img");
    				/*设置按钮居中*/
    				navContainer.style.left = (outer.offsetWidth - navContainer.offsetWidth)/2 + "px";
    				var tempBtnTop = (outer.offsetHeight - picLbtn.offsetHeight)/2 + "px";
    				picLbtn.style.top = tempBtnTop;
    				picRbtn.style.top = tempBtnTop;
    				//-- 获取元素样式,最低兼容ie8
    				function getStyle(obj, name) {
    					if(window.getComputedStyle) {
    						return getComputedStyle(obj, null)[name];
    					} else {
    						return obj.currentStyle[name];
    					}
    				}
    				//-- 获取outer的宽度
    				var getOuterWidth = getStyle(outer,"width");
    				var widthObject = getOuterWidth.match(/d*/);
    				var width = widthObject[0];
    				//-- 根据图片的数量设置ul的总宽度
    				imgList.style.width = width*imgArr.length+"px";
    				function move(obj, attr, target, speed, callback){
    					clearInterval(obj.timer);
    					var current = parseInt(getStyle(obj, attr));
    					if(current > target) {
    						speed = -speed;
    					}
    					obj.timer = setInterval(function(){
    						var oldValue = parseInt(getStyle(obj, attr));
    						var newValue = oldValue + speed;
    						if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)) {
    							newValue = target;
    						}
    						obj.style[attr] = newValue + "px";
    						if(newValue == target) {
    							clearInterval(obj.timer);
    							callback && callback();
    						}
    					}, 30);
    				}
    				//设置默认选中的效果
    				var index = 0;
    				var allA = document.querySelectorAll("#navContainer>a");
    				allA[index].style.backgroundColor = "black";
    				//-- 正常开启自动切换函数
    				autoChange();
    				function setA(){
    					if(index >= imgArr.length - 1){
    						index = 0;
    						imgList.style.left = 0;
    					}
    				}
    				function setRed(){
    					for(var i=0 ; i<allA.length ; i++){
    						allA[i].style.backgroundColor = "";
    					}
    					allA[index].style.backgroundColor = "black";
    				}
    				var timer;
    				//--自动切换图片
    				function autoChange(){
    					timer = setInterval(function(){
    						index++;
    						index %= imgArr.length;
    						move(imgList , "left" , -width*index , 20 , function(){
    							setA();
    							setRed();
    						});
    					},3000);
    				}
    				//--实现点击导航点切换图片
    				//--调用setA、move、autochange函数
    				for(var i=0; i<allA.length ; i++){
    					allA[i].num = i;
    					allA[i].onclick = function(){
    						clearInterval(timer);
    						index = this.num;
    						setRed();
    						move(imgList , "left" , -width*index , 20 , function(){
    							autoChange();
    						});
    					}
    				picLbtn.onclick = function(){
    					clearInterval(timer);
    					if (index != 0){
    						index = index-1;
    					}
    					setRed();
    					move(imgList , "left" , -width*index , 20 , function(){
    						autoChange();
    					});
    				}
    				picRbtn.onclick = function(){
    					clearInterval(timer);
    						if (index != allA.length - 1) {
    							index = index+1;
    						}
    						setRed();
    						move(imgList , "left" , -width*index , 20 , function(){
    							autoChange();
    						});
    					}
    				}
    				var startX = 0;
    				var moveX = 0;
    				var distanceX = 0;
    				var isMove = false;
    				outer.addEventListener('touchstart', function(e){
            			clearInterval(timer); //--清除定时器,要记得事件结束之后再打开
            			startX = e.touches[0].clientX;  //--触摸点的横坐标
       				});
    				outer.addEventListener('touchmove',function(e){
    	        		moveX = e.touches[0].clientX;//--获取当前手的横坐标
    	        		distanceX = moveX - startX; //--移动的距离=现在-初始
    	        		isMove = true;//证明滑动过
    					move(imgList , "left" , -width*index+distanceX , 20 , function(){});
    	    		});
    				outer.addEventListener('touchend', function(){
    	        		if(isMove && Math.abs(distanceX) > width/3){
    	            		if(distanceX > 0 && index != 0){
    	                		index = index - 1;
    	        			}
    	        			else if(distanceX < 0 && index != imgArr.length - 2){
    	            			index = index + 1;
    	        			}
    	        			move(imgList , "left" , -width*index, 20 , function(){});
    	        		}
    	        		else if(isMove && Math.abs(distanceX) < width/3){
    	        			move(imgList , "left" , -width*index, 20 , function(){});
    	        		}
    	        		setRed();
    	        		autoChange();
    	    		});
    			}
    		</script>
    	</head>
    	<body>
    		<div id="outer">
    			<ul id="imgList">
    				<li><img src="img/pic/1.jpg"/></li>
    				<li><img src="img/pic/2.jpg"/></li>
    				<li><img src="img/pic/3.jpg"/></li>
    				<li><img src="img/pic/4.jpg"/></li>
    				<li><img src="img/pic/5.jpg"/></li>
    				<li><img src="img/pic/1.jpg"/></li>
    			</ul>
    			<div id="navContainer">
    				<a href="javascript:;"></a>
    				<a href="javascript:;"></a>
    				<a href="javascript:;"></a>
    				<a href="javascript:;"></a>
    				<a href="javascript:;"></a>
    			</div>
    			<a href="javascript:;" id="picLbtn"><img src="img/pic/picLbtn.png"></a>
    			<a href="javascript:;" id="picRbtn"><img src="img/pic/picRbtn.png"></a>
    		</div>
    	</body>
    </html>
    

    缺点和解决(这个一定要看!!!):

    缺点:切换到手机端的时候:点击导航点和左右切换按钮会出现问题

    原因:这个是因为系统把点击导航点和按钮也当成了滑动事件,这样就要同时相应两组事件,所以会出问题(俺是这么理解的,对不对也不知道)

    解决:解决就很简单了,反正手机端也不需要点击导航点和切换按钮(没有人会在手机上用那玩意)

       所以如果是用在响应式布局的话,判断一下屏幕宽度,小于660px:左右按钮display:none,然后把导航点事件移除,大于660px就什么都不做

       如果用在纯手机端,就直接把导航点的事件和左右按钮代码删掉就行了

       这个程序写的还是有点模块化思想的,四五六步的内容随便删掉哪个其他互不影响。

    结尾:

    最后奉上B站视频教程的链接https://www.bilibili.com/video/av34087791,上文的大部分内容都来自这组视频~

  • 相关阅读:
    我的浏览器和常用扩展
    Win10安装.Net Framework4.7及更高版本
    压缩和解压工具bandizip
    Oracle trunc()函数的用法
    Oracle 中 decode 函数用法
    Js/Jquery获取iframe中的元素 在Iframe中获取父窗体的元素方法
    String literal is not properly closed by a double-quote eclipse
    linux 启动 Oracle 实例
    查询当前Oracle数据库的实例
    sqlplus 执行 sql 文件
  • 原文地址:https://www.cnblogs.com/iszhangk/p/10923650.html
Copyright © 2011-2022 走看看