使用JavaScript實(shí)現(xiàn)圖片輪播效果

原理

圖片輪播原理:
將一系列大小相等的圖片平鋪,利用css布局只顯示一張圖片,其他圖片隱藏,通過計(jì)算偏移量利用定時(shí)器自動(dòng)切換圖片,或者手動(dòng)點(diǎn)擊切換圖片。

樣式布局

關(guān)于圖片列表的布局,這里在js中使用style.left這個(gè)屬性計(jì)算偏移量來進(jìn)行圖片的切換。而在style.left這個(gè)屬性中,是無法識(shí)別樣式表中寫的left值,只對(duì)HTML中寫的left值有效.
關(guān)于小圓點(diǎn)按鈕,使用自定義屬性index來標(biāo)識(shí)第幾個(gè)小圓點(diǎn)。后邊當(dāng)點(diǎn)擊某個(gè)小圓點(diǎn)時(shí),可獲取該小圓點(diǎn)的index值來知道小圓點(diǎn)的位置。利用css樣式使class="on"的元素顏色變亮,即點(diǎn)亮當(dāng)前點(diǎn)擊到的小圓點(diǎn)。
關(guān)于箭頭切換,使用鏈接形式,可以添加hover偽類變換樣式。<左箭頭;>右箭頭,html轉(zhuǎn)義字符。

無縫切換:最后一張圖片切換到第一張圖片時(shí),會(huì)出現(xiàn)一片空白,這里,借助兩張輔助圖來填補(bǔ)這片空白。
即將最后一張圖片屬性復(fù)制到第一張圖片前,將第一張圖片屬性信息復(fù)制到最后一張圖片后面。
并且,將第一張圖片輔助圖(實(shí)際上是實(shí)際顯示的第5張圖片隱藏起來,故設(shè)置style="left: -600px;"

<div id="container" >
        <!-- 圖片列表 -->
        <div id="list" style="left: -600px">
            ![](img/5.jpg)<!-- 第一張輔助圖 -->
            ![](img/1.jpg)
            ![](img/2.jpg)
            ![](img/3.jpg)
            ![](img/4.jpg)
            ![](img/5.jpg)
            ![](img/1.jpg)<!-- 第二張輔助圖 -->   
        </div>
        <!-- 小圓點(diǎn)按鈕 -->
        <div id="buttons">
            <span index="1" class="on"></span>
            <span index="2"></span>
            <span index="3"></span>
            <span index="4"></span>
            <span index="5"></span>
        </div>
        <!-- 箭頭切換 -->
        <a href="javascript:;" class="arrow" id="prev">&lt;</a>
        <a href="javascript:;" class="arrow" id="next">&gt;</a>
    </div>

css結(jié)構(gòu):

  1. 絕對(duì)定位問題,圖片和原點(diǎn)的浮動(dòng)屬性;
  2. overflow: hidden;//溢出隱藏
  3. 確保每個(gè)小圓點(diǎn)所在層置頂,(z-index:999;)這里設(shè)置為z-index:2; 另外z-index 僅能在定位元素上奏效
  4. cursor CSS屬性定義鼠標(biāo)指針懸浮在元素上方顯示的鼠標(biāo)光標(biāo)。cursor: pointer顯示為小手。
*{
    margin: 0;
    padding: 0;
    text-decoration: none;
}
body{
    padding: 10px;
}
#container{
    width: 600px;
    height: 400px;
    border: 3px solid #333;
    overflow: hidden;//溢出隱藏
    position: relative;//定位

}
#list{
    width: 4200px;//所有圖片平鋪的寬度
    height: 400px;
    border: 3px solid #333;
    position: absolute;//定位問題 利用偏移量進(jìn)行切換
    /*z-index: 1;*/

    /* transition:left 2s;
    
        -moz-transition:left 2s; Firefox 4
    
        -webkit-transition:left 2s; Safari and Chrome
    
        -o-transition:left 2s; Opera */
}
#list img{
    float: left;//浮動(dòng)(不用清除浮動(dòng))

}
#buttons{
    position: absolute;
    /*height: 20px;
    width: 100px*/
    /*z-index: 2;*/ //層級(jí),使按鈕置于頂層
    bottom: 25px;
    left: 250px;
}
#buttons span{
    cursor: pointer;/*小手*/
    float: left;//浮動(dòng)                        
    border: 1px solid #fff;//白邊框
    width: 20px;
    height: 20px;
    border-radius: 50%;//圓角
    background: #333;//黑色
    margin-right: 5px;
}
#buttons .on{
    background: orangered;//點(diǎn)亮成橘色
}
.arrow{
    cursor: pointer;
    display: none;//默認(rèn)不顯示箭頭
    line-height: 39px;//垂直居中
    text-align: center;//水平居中

    font-size: 36px;
    font-weight: bold;

    width: 40px;
    height: 40px;
    position: absolute;//絕對(duì)定位
    /*z-index: 2;*/
    top: 180px;//高度
    background-color: rgba(0,0,0,0.3);
    color: #fff;
}
.arrow:hover{
    background-color: rgba(0,0,0,0.7);//鼠標(biāo)移上去顏色加深
}
#container:hover .arrow{
    display: block;//鼠標(biāo)移到盒子上顯示箭頭
}
#prev{
    left: 20px;//單獨(dú)定義左箭頭水平位置
} 
#next{
    right: 20px;//單獨(dú)定義右箭頭水平位置
}

js部分整個(gè)功能封裝在window.onload事件中。

箭頭切換&無限滾動(dòng)&動(dòng)畫切換

先獲取元素,添加點(diǎn)擊事件。parseInt將字符串轉(zhuǎn)換成數(shù)字。
獲取style.left,是相對(duì)左邊獲取距離,且style.left獲取的是字符串,需要用parseInt()取整轉(zhuǎn)化為數(shù)字。

    next.onclick=function(){
         list.style.left=parseInt(list.style.left)-600+'px';
    }
    prev.onclick=function(){
        list.style.left=parseInt(list.style.left)+600+'px';
    }

封裝在函數(shù)animate里。并實(shí)現(xiàn)循環(huán)切換。當(dāng)切換到第一張輔助圖上時(shí),parseInt(list.style.left)的值為0,實(shí)際上應(yīng)該是最后一張圖片,所以歸為到最后一張圖片的位置上list.style.left=-3000+'px';
同理,當(dāng)切換到第二張輔助圖上時(shí),parseInt(list.style.left)的值為-3600,實(shí)際上應(yīng)該是第一張圖片,所以歸為到第一張圖片的位置上list.style.left=-600+'px';

    //切換圖片,利用圖片的偏移量
    function animate(offset){
        var newLeft = parseInt(list.style.left)+offset;//值 圖片左側(cè)距離父元素的值
        list.style.left=newLeft+'px';//px
        // list.style.left=list.offsetLeft+offset+'px';
        if(newLeft>-600){//和第一張圖片位置比較
            list.style.left=-3000+'px';//歸位到第五張圖片
        }
        if(newLeft<-3000){//和最后一張圖片位置比較
            list.style.left=-600+'px';//歸位到第一張圖片
        }
        // debugger;
}

實(shí)現(xiàn)每次切換圖片的過程中,是過渡動(dòng)畫切換的。
思路:通過自定義位移完一張圖片的總時(shí)間time和每次位移的時(shí)間inteval,來計(jì)算圖片移動(dòng)一下的距離speed,直到一張圖片完整的移到當(dāng)前可視的盒子區(qū)域。

animate()函數(shù)內(nèi)定義go()函數(shù),首先判斷圖片是往哪個(gè)方向移,因?yàn)槿绻蜃笠频脑挘磦魅氲钠屏渴秦?fù)值,所以speed < 0,這時(shí)候圖片左側(cè)距離父元素的值parseInt(list.style.left)是越來越小的,直到為newLeft,結(jié)束動(dòng)畫位移,一張圖片完整的切換到了可視區(qū)域。so只要parseInt(list.style.left) >newLeft,就進(jìn)行動(dòng)畫位移;

同理,當(dāng)圖片向右移時(shí),speed >0,這時(shí)候圖片左側(cè)距離父元素的值parseInt(list.style.left)是越來越大的,直到為newLeft。所以只要parseInt(list.style.left) < newLeft,就進(jìn)行著動(dòng)畫位移。

如果滿足動(dòng)畫位移條件會(huì)一直執(zhí)行,使用定時(shí)器setTimeout(go, inteval),沒隔一段時(shí)間interval執(zhí)行一次go
這里使用了遞歸,函數(shù)內(nèi)部調(diào)用該函數(shù)。

對(duì)于定時(shí)器,注意setInterval()跟setTimeout()的區(qū)別。簡單來說,setInterval()執(zhí)行多次,setTimeout()只執(zhí)行一次。
更具體的用法可以點(diǎn)擊鏈接查看區(qū)別:window.setInterval window.setTimeout

這里定義一個(gè)初始值var animated = false;,表示默認(rèn)不進(jìn)行動(dòng)畫位移,animated = true表示開始動(dòng)畫位置標(biāo)志。并在結(jié)束動(dòng)畫位移操作時(shí),將animated的值置為false。

function animate (offset) {
         animated = true;//開始動(dòng)畫位移
         var time = 300;//一張圖片位移總時(shí)間
         var inteval = 10;//位移間隔時(shí)間
         var speed = offset/(time/inteval);//求得一次位移的距離
         var newLeft = parseInt(list.style.left) + offset;//圖片左側(cè)距離父元素的值

        //動(dòng)畫函數(shù)
         var go = function (){
        //監(jiān)測(cè)是否具備進(jìn)行動(dòng)畫位移的條件
        //判斷左切換和右切換兩種情況,都并且一張圖片沒有完整的占滿盒子區(qū)域時(shí)。
             if ( (speed > 0 && parseInt(list.style.left) < newLeft) || (speed < 0 && parseInt(list.style.left) >newLeft)) {
                   list.style.left = parseInt(list.style.left) + speed + 'px';
                   setTimeout(go, inteval);//遞歸
              }else {
                    list.style.left = newLeft + 'px';
                    if(newLeft>-600){
                        list.style.left=-3000+'px';//歸位到第五張圖片
                     }
                    if(newLeft<-3000){
                    list.style.left=-600+'px';//歸位到第一張圖片
                    }
                    // debugger;
                    animated = false;//結(jié)束動(dòng)畫位移
             }
          }
        go();//函數(shù)調(diào)用
   }

優(yōu)化:加入下面代碼,當(dāng)傳入的偏移量為0時(shí),animate()函數(shù)里面的部分不需再執(zhí)行了。

  if (offset == 0) {
                    return;
    }

按鈕切換

先設(shè)置初始值index的值為1.即默認(rèn)第一個(gè)小圓點(diǎn)時(shí)點(diǎn)亮著的,index用來標(biāo)識(shí)第幾個(gè)小圓點(diǎn)。點(diǎn)擊箭頭時(shí)增加或者減小index的值來實(shí)現(xiàn)切換小圓點(diǎn)的功能。注意index的取值是1~5,所以當(dāng)index的值加到5時(shí),要回歸到1;index的值為1時(shí),要切到5.

實(shí)現(xiàn)思路:先判斷index的值,右箭頭觸發(fā)點(diǎn)擊事件里如果當(dāng)前index的值為5,就使index=1,否則index+=1;,同理,左箭頭觸發(fā)點(diǎn)擊事件里如果當(dāng)前index的值為1,就使index=5,否則index-=1;
然后點(diǎn)亮相應(yīng)的小圓點(diǎn)showButton()

    var index = 1;
    next.onclick=function(){
        animate(-600);
        if(index==5){
            index=1;//使index的值不超過5
        }else{
            index+=1;//index的值隨著箭頭的點(diǎn)擊進(jìn)行更新
        }
        showButton();
    }

    prev.onclick=function(){
        animate(600);   
        if(index==1){
            index=5;//使index的值不小于1
        }else{
            index-=1;
        }
        showButton();

    }

優(yōu)化:在兩個(gè)點(diǎn)擊事件中先加入以下代碼。當(dāng)檢測(cè)到animated=true即進(jìn)行著動(dòng)畫位移,不執(zhí)行其他代碼,避免卡頓優(yōu)化性能。

if (animated) {
         return;
  }

獲取小圓點(diǎn)var buttons = document.getElementById('buttons').getElementsByTagName('span');,不止一個(gè),是一個(gè)數(shù)組類型。數(shù)組從0開始,所以index -1.
因?yàn)閏ss樣式中#buttons .on{background: orangered;},所以這里利用className= 'on'來點(diǎn)亮小圓點(diǎn)。


    //點(diǎn)亮小圓點(diǎn)
    function showButton(){
        buttons[index -1].className = 'on';//點(diǎn)亮
    }

這樣會(huì)出現(xiàn)一個(gè)問題,切換到下一個(gè)小圓點(diǎn)時(shí),前面點(diǎn)擊過的小圓點(diǎn)依然是點(diǎn)亮著的。所有在showButton()函數(shù)中要先清除之前的樣式。

for(var i=0;i<buttons.length;i++){//遍歷
            if(buttons[i].className=='on'){//關(guān)閉其他亮著的小圓點(diǎn)
                buttons[i].className='';
                // break;//一旦監(jiān)測(cè)到就退出循環(huán)
            }
        }

這樣就實(shí)現(xiàn)了點(diǎn)擊箭頭切換圖片時(shí),對(duì)應(yīng)的小圓點(diǎn)也跟著進(jìn)行切換。

===
下面實(shí)現(xiàn)點(diǎn)擊小圓點(diǎn)時(shí),圖片切換的功能
思路:仍然是利用圖片偏移量進(jìn)行切換,偏移值為-600乘以要點(diǎn)擊的小圓點(diǎn)的index值減去當(dāng)前小圓點(diǎn)的index值)

因?yàn)橐獙?duì)每一個(gè)小圓點(diǎn)添加點(diǎn)擊事件,先對(duì)其進(jìn)行遍歷。
首先獲取要點(diǎn)擊的(目標(biāo))小圓點(diǎn)的自定義屬性值parseInt(this.getAttribute('index')),即在html布局中span標(biāo)簽里index的屬性。

getAttribute()既可以獲取自定義屬性值也可以獲取原本就存在的屬性的值。

不要忘記更新當(dāng)前的index值index=myIndex;

    //點(diǎn)擊小圓點(diǎn)進(jìn)行切換圖片
    for (var i = 0; i < buttons.length; i++) {
        buttons[i].onclick = function(){//對(duì)每個(gè)小圓點(diǎn)添加點(diǎn)擊事件
        var myIndex = parseInt(this.getAttribute('index'));//獲取要點(diǎn)擊的小圓點(diǎn)的自定義index屬性值
        console.log(myIndex);
        var offset = -600*(myIndex-index);//求偏移值
        animate(offset);
        index=myIndex;//更新到當(dāng)前的index值
        showButton();
        }
        // debugger;
    }

優(yōu)化:在每個(gè)小圓點(diǎn)的點(diǎn)擊事件中加入以下代碼。

點(diǎn)擊小圓點(diǎn)時(shí),當(dāng)檢測(cè)到animated=true即進(jìn)行動(dòng)畫位移的過程中,不觸發(fā)該點(diǎn)擊事件即不執(zhí)行后面代碼,避免卡頓優(yōu)化性能。

當(dāng)檢測(cè)到this.className=='on'即點(diǎn)擊當(dāng)前的小圓點(diǎn),也不需要執(zhí)行后面代碼,優(yōu)化性能。

if (animated) {
     return;
 }
if(this.className=='on'){
     return;    
 }  

自動(dòng)播放

利用定時(shí)器setTimeout()setInterval,實(shí)現(xiàn)每隔3秒自動(dòng)播放下一張圖片。并在鼠標(biāo)移到var container = document.getElementById('container');上時(shí)清楚浮動(dòng)。

用到一種回調(diào)函數(shù)的使用方式,如果stop(),stop方法就被執(zhí)行了,但是如果寫方法名stop,是事件觸發(fā)時(shí)才會(huì)調(diào)用stop方法

var interval = 3000;
var timer;
function play() {
        timer = setTimeout(function () {
              next.onclick();
              play();
        }, interval); 
}
function stop() {
        clearTimeout(timer);
 }
/*
    //每隔3秒自動(dòng)播放下一張圖片
    function play(){
        timer = setInterval(function(){
            next.onclick();
        },3000);
    }
    //清除定時(shí)器
    function stop(){
        clearInterval(timer);
    }
*/

container.onmouseover = stop;
container.onmouseout = play;
play();

最后,如果不進(jìn)行一些優(yōu)化,可能會(huì)出現(xiàn)瘋狂點(diǎn)擊會(huì)使圖片錯(cuò)位等情況。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評(píng)論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,585評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 通過學(xué)習(xí),我理解了圖片輪播原理,學(xué)習(xí)了setTimeout()、setInterval()函數(shù)設(shè)置定時(shí)器與清除定時(shí)...
    McRay閱讀 2,178評(píng)論 0 7
  • 進(jìn)入前端將近一年了,js還是很弱,突發(fā)奇想寫一個(gè)輪播圖,就找到了這個(gè)博主的材料,和大家分享。 輪播圖的原理: 一系...
    FRRRR閱讀 3,676評(píng)論 0 11
  • 前端學(xué)習(xí)時(shí)間不長,最近看完了第一遍高設(shè),想著試著寫一點(diǎn)東西,于是有了這個(gè)還不算好的輪播效果。(學(xué)習(xí)出處為慕課網(wǎng)) ...
    JellyFive閱讀 624評(píng)論 0 6
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,774評(píng)論 1 92
  • 是否有感冒可能了?
    時(shí)間閱讀 157評(píng)論 0 0