原生js實現響應式輪播圖,支持電腦端點擊手機端滑動切圖

輪播圖的實現原理並不難,可是步驟有些繁瑣。最近練習了一個輪播圖,大部分是跟着網上的教程寫的,而後本身作了一點兼容ie8的修改,加了點擊切換圖片的特效和手機端的滑動特效,讓這個輪播圖能夠在響應式的網站中使用,同時兼容pc端和觸屏端。javascript

輪播圖的樣式也分不少種,淡入淡出的輪播圖很容易實現,只須要把圖片全都疊在一塊兒,讓相應的圖片輪流顯示就好了,可是滾動能的輪播圖就要複雜不少。這裏介紹的是滾動的輪播圖:css

原理:

實現的原理就是將全部的圖片橫向的排列起來,排成一個長方形,讓這個長方形的整體不斷平移,從而使圖片輪流顯示在div中,而後將div以外的隱藏起來就能夠了html

下面是詳細步驟:java

第一步,基本骨架

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的寬高,這樣作不但可以提升開發的效率,還更便於網站的維護,即便圖片源的尺寸都是不同的,也能正常顯示:app

//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也是能夠的:dom

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

這裏把a標籤的數量寫固定了,若是上面放置了十張圖片輪播,下面就要寫十個導航點。ide

能夠根據圖片的數量動態的設置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{
				width: 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{
				width: 500px;
				height: 333px;
			}
			#navContainer{
				position: absolute;
				bottom: 15px;
			}
			
			#navContainer>a{
				z-index: 5;
				float: left;
				width: 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,上文的大部份內容都來自這組視頻~

相關文章
相關標籤/搜索