天天看點

[ES6] 輪播圖---面向對象版本

1,  資料 函數程式 都定義在 執行個體化對象中
      通過調用 執行個體化對象的函數方法 
      調用 執行個體化對象中 存儲的資料
      執行程式 實作效果
  
  2,  this指向必須是執行個體化對象
          匿名函數 --- 箭頭函數

          回調函數 通過 bind() 文法修改設定 this指向

          提前定義一個變量 存儲this指向
              一個函數中 要是用 多個this指向
              提前使用變量儲存不同的this
           

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }

        ul,ol,li{
            list-style: none;
        }

        a,a:hover,a:active{
            text-decoration: none;
        }

        img{
            width: 100%;
            height: 100%;
            display: block;
        }

        .banner{
            width: 600px;
            height: 400px;
            border: 5px solid #000;
            position: relative;
            margin: 50px auto;
            /* overflow: hidden; */
        }

        .banner>ul{
            width: 500%;
            height: 100%;
            position: absolute;
            top:0;
            left:0;
        }

        .banner>ul>li{
            float: left;
            width: 600px;
            height: 400px;
        }

        .banner>ol{
            height: 50px;
            position: absolute;
            bottom:50px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            border-radius: 15px;
        }

        .banner>ol>li{
            width: 20px;
            height:20px;
            border-radius: 50%;
            background: #fff;
            margin: 0 15px;
            cursor: pointer;
        }

        .banner>ol>li.active{
            background: red;
        }

        .banner>div{
            width: 100%;
            height: 50px;
            position: absolute;
            left:0;
            top: 50%;
            transform: translateY(-50%);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .banner>div>a{
            display: flex;
            justify-content: center;
            align-items: center;
            width: 50px;
            height: 50px;
            font-size: 40px;
            color: #fff;
        }

    </style>
</head>
<body>
    <!-- 輪播圖div -->
    <div class="banner">
        <!-- 輪播圖内容 -->
        <ul></ul>

        <!-- 焦點按鈕 -->
        <ol></ol>

        <!-- 左右切換按鈕 -->
        <div>
            <a href="JavaScript:;" name="left">&lt;</a>
            <a href="JavaScript:;" name="right">&gt;</a>
        </div>
    </div>

    <!-- 導入外部檔案加載move運動函數 -->
    <script src="./move.js"></script>
    <!-- 導入外部js檔案中的構造函數 -->
    <script src="./banner.js"></script>
    <script>
        // 面向對象輪播圖
        // 加載外部js檔案導入其中定義的構造函數

        // 定義數組
        const bannerArr = [
            {id:1 , width:500 , height:333 , size:29.7 , name:'1.jpg'},
            {id:2 , width:500 , height:333 , size:19.3 , name:'2.jpg'},
            {id:3 , width:500 , height:333 , size:17.1 , name:'3.jpg'},
            {id:4 , width:500 , height:333 , size:20.3 , name:'4.jpg'},
            {id:5 , width:600 , height:400 , size:320 , name:'5.jpg'},
        ];

        // 擷取 整個父級标簽對象 .banner
        const oDivBanner = document.querySelector('.banner');

        // 調用執行 構造函數 建立執行個體化對象
        // 參數1: 标簽對象名稱
        // 參數2: 需要的數組資料
        const bannerObj = new CreateBanner( oDivBanner , bannerArr);
        console.log( bannerObj );

        // 隻需要調用一個入口函數就可以了
        bannerObj.init();

    </script>

</body>
</html>
           

banner.js

class CreateBanner{
    // 構造器
    constructor(element , imgArr){
        // 定義屬性 存儲 參數資料
        this.ele = element;
        this.arr = imgArr;
        // 屬性存儲資料
        this.ul = element.querySelector('ul');
        this.ol = element.querySelector('ol');
        // 原始數組單元個數
        this.length = imgArr.length ;
        // 定義變量 存儲顯示li的索引下标
        // 初始值 是 1
        this.index = 1;
        // 定義變量 存儲資料防止點選過快
        this.bool = true; 

        // 隻定義不指派
        this.ulLis ; 
        this.olLis ;
        this.liWidth ;
        this.time ; 
    }

    // 定義一個入口函數 
    // 調用這個函數 函數中調用執行所有需要運作的函數
    // 入口函數一般叫 init
    init(){
        this.setPage();
        this.autoLoop();
        this.setMouse();
        this.setClick();
        this.setHide();
    }

    // 定義函數方法

    // 生成頁面
    setPage(){
        // 定義字元串
        let ulStr = '' ;
        let olStr = '' ;

        // 循環周遊數組
        // item 是 數組中存儲的資料對象
        this.arr.forEach( (item , key)=>{
            // 生成 ul>li 标簽内容
            ulStr += `<li><img src="./images/${item.name}"></li>`;
            olStr += key === 0 ? `<li class="active" name="focus" num="${key}"></li>` : `<li name="focus" num="${key}"></li>`;
        });

        // 寫入頁面
        this.ul.innerHTML = ulStr;
        this.ol.innerHTML = olStr;

        // 擷取 所有的 ul>li ol>li
        // 給已經定義的屬性指派
        this.ulLis = this.ul.querySelectorAll('li');
        this.olLis = this.ol.querySelectorAll('li');

        // 擷取li标簽寬度指派給已經定義的屬性
        this.liWidth = parseInt( window.getComputedStyle(this.ulLis[0]).width ) ; 

        // 克隆 ul>li 的第一個和最後一個
        const cloneFirst = this.ulLis[0].cloneNode(true);
        const cloneLast = this.ulLis[this.ulLis.length-1].cloneNode(true);
    
        // 克隆第一個寫入 ul最後
        // 克隆最後一個寫入 ul第一個
        this.ul.appendChild( cloneFirst );
        this.ul.insertBefore( cloneLast , this.ulLis[0] );

        // 設定ul标簽寬度
        // 原始數組單元個數+2  乘以 一個li寬度
        this.ul.style.width = ( this.length + 2 ) * this.liWidth + 'px';
    
        // 将 ul 向左定位一個li寬度
        this.ul.style.left = -this.liWidth + 'px';
    
    }

    // 自動輪播
    autoLoop(){
        // 定義定時器
        this.time = setInterval( ()=>{

            // 變量累加1
            this.index++;

            // 在運動之前 先切換 焦點按鈕 css樣式
            this.focusStyle();

            // 調用move運動函數,改變ul定位
            // 每次定位的資料是 顯示li索引下标*一個li寬度

            // 回調函數的this執行也會改變 一般是 undefined 或者 window
            // 使用 bind方法 修改this指向 為 目前this中存儲的指向 也就是 執行個體化對象
            move( this.ul , {left:-this.index*this.liWidth} , this.loopEnd.bind(this) );
        } , 4000 )
    }

    // 運動停止的回調函數
    loopEnd(){

        // 判斷index的數值
        // 如果 index的數值是 所有li的最後一個 
        // index 指派 1 
        // 運動結束 從最後一個li 瞬間定位到 第二個li
        if( this.index === this.length+1 ){
            this.index = 1 ; 
            this.ul.style.left = -this.index*this.liWidth + 'px';

        // index 是 0 瞬間定位到 倒數第二個li
        }else if( this.index === 0 ){
            this.index = this.length ; 
            this.ul.style.left = -this.index*this.liWidth + 'px';
        }

        // 當所有的運動結束 
        // 給 bool變量指派 true 可以再次觸發move()運動函數
        this.bool = true ;
    }

    // 焦點樣式函數
    focusStyle(){
        // 提前定義一個變量 存儲this指向
        const _this = this ;

        // 給所有的ol>li清除class,active
        this.olLis.forEach(function(item,key){
            // item 是 每一個ol>li标簽
            item.classList.remove('active');
            // 如果 key索引下标 和 index-1 相同 添加class,active

            // 使用 _this中 存儲的 指向 作為 調用資料
            if( key === _this.index-1 ){
                item.classList.add('active');
            }
        })

        // 特殊情況:
        // 最後一個ul>li 給 第一個ol>li添加樣式
        // 第一個ul>li 給 最後一個 ol>li添加樣式
        if( this.index === this.length+1 ){
            this.olLis[0].classList.add('active');
        }else if( this.index === 0 ){
            this.olLis[this.olLis.length-1].classList.add('active');
        }
    }

    // 滑鼠移入移出
    setMouse(){
        // 移入父級div标簽 清除 定時器
        // 移出父級div标簽 調用 自動輪播函數
        this.ele.addEventListener('mouseenter' , ()=>{
            clearInterval( this.time );
        })

        this.ele.addEventListener('mouseleave' , ()=>{
            this.autoLoop();
        })
    }

    // 點選效果
    setClick(){
        // 給父級div添加點選事件 通過事件委托添加點選效果
        this.ele.addEventListener('click' , e=>{
            if( e.target.getAttribute('name') === 'left' ){
                // 防止點選過快
                if( this.bool ){
                    this.bool = false;
                }else{
                    return ;
                }


                // 索引下标--
                this.index--;

                // 設定焦點樣式
                this.focusStyle();

                // 通過move運動函數切換顯示li
                move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
            
            }else if( e.target.getAttribute('name') === 'right' ){
                // 防止點選過快
                if( this.bool ){
                    this.bool = false;
                }else{
                    return ;
                }

                // 索引下标++
                this.index++;

                // 設定焦點樣式
                this.focusStyle();

                // 通過move運動函數切換顯示li
                move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
            
            }else if( e.target.getAttribute('name') === 'focus' ){
                // 防止點選過快
                if( this.bool ){
                    this.bool = false;
                }else{
                    return ;
                }

                // 索引下标指派 點選标簽索引下标+1
                this.index = e.target.getAttribute('num')-0 + 1 ;

                // 設定焦點樣式
                this.focusStyle();

                // 通過move運動函數切換顯示li
                move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
            }
        })
    }

    // 浏覽器最小化
    setHide(){
        document.addEventListener('visibilitychange' , ()=>{
            if( document.visibilityState === 'hidden' ){
                // 清除定時器,終止輪播圖自動運作
                clearInterval(this.time);
            }else if( document.visibilityState === 'visible' ){
                // 再次調用自動輪播函數
                this.autoLoop();
            }
        })
    }


}   
           

move.js

// 參數1: 運動的标簽對象
// 參數2: 對象形式 屬性是要運動的css屬性 屬性值是要運動的css樣式的最終值
// 參數3: 存儲要執行的函數程式 預設值是空函數
function move(element, object, callback = function () { }) {
    // 定義一個變量 存儲 參數2 對象中 單元個數
    let num = 0;

    // 使用 for...in 循環周遊 參數2對象
    // 定義的變量 存儲對象的鍵名 也就是 left,top.width,height,opacity...
    // 對象[變量] 擷取對象鍵名存儲的鍵值 也就是 最終值 500 300 0.3....
    // 目前變量必須要使用 let 關鍵詞來定義
    for (let type in object) {
        // 每次循環 給 變量累加1 表示對象參數有一個單元
        num++;

        // 之前的type參數是 現在 for..in循環 type變量
        // 之前的最終值是   現在 對象[變量] 擷取的資料

        // 擷取運動屬性的初始值
        // 如果是 透明度 直接擷取結果 * 100
        // 不是   透明度 結果 parseInt() 取整
        let startVal = type === 'opacity' ? window.getComputedStyle(element)[type] * 100 : parseInt(window.getComputedStyle(element)[type]);

        // 如果 是   透明度 最終值 * 100 
        // 如果 不是 透明度 最終值就是本身
        // 之前的最終值 是 目前 對象參數中,屬性存儲的屬性值
        let endVal = type === 'opacity' ? object[type] * 100 : object[type];

        // 設定定時器
        let time = setInterval(function () {
            // 計算步長
            let step = (endVal - startVal) / 10;

            // 步長取整 
            step = step > 0 ? Math.ceil(step) : Math.floor(step);

            // 初始值累加步長值
            startVal += step;

            // 新的初始值 指派給标簽對象的css
            // 如果是   透明度 指派 目前累加之後的初始值 除以 100
            // 如果不是 透明度 指派 目前累加之後的初始值 拼接 px機關
            element.style[type] = type === 'opacity' ? startVal / 100 : startVal + 'px';

            // 判斷 如果初始值 等于 最終值
            if (startVal === endVal) {
                // 清除定時器
                clearInterval(time);
                // 給 變量 --
                num--;

                // 當num數值是 0 時 
                // 表示所有執行的定時器都被清除了
                // 也就是所有css運動都執行結束了
                if (num === 0) {
                    // 執行回調函數
                    callback();
                }

            }
        }, 30)
    }
}
           

注意:

  • 将 匿名函數 function(){}/forEach/定時器/事件綁定 修改為 箭頭函數 ()=>{}
  • 回調函數 使用 bind方法 設定this指向

    this.loopEnd.bind(this)