天天看點

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

什麼叫無縫輪播

無縫輪播是網頁中比較常見的一種輪播方式,很多網站首頁都應用了無縫輪播,這種效果使頁面看起來更順滑。

先來看一下我實作的效果圖:

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

無縫輪播的實作

下面用示意圖清楚的展現了無縫輪播的原理:

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

一共有3張圖檔輪播,但是為什麼有5張圖檔呢?這是因為要想實作無縫輪播的效果,就要用“障眼法”。

每當圖檔滾動到最後一張或者第一張時,都會自動回到第一張或最後一張,加入這兩張圖檔有一個過渡的效果。

原理分析

好了,在介紹了無縫輪播之後,小夥伴們是不是躍躍欲試了呢,别着急,在正式寫代碼之前讓我們先把邏輯理清楚。

假設目前正在播放的圖檔是1,令

currentIndex = 1

,目前left = 實際運動過程中距離左邊的長度,目标left = 要切換的圖檔距離左邊的長度,且這兩個均為負數。

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

如果目前圖檔移動方向是向左,當

目标left < 目前left

時,假設是從0移動到2:

//目标left,index是要切換的圖檔的索引,imgWidth是圖檔寬度
var newLeft = index * imgWidth;
//計算需要移動的總距離
var distance = newLeft - marginLeft;
           

目标left > 目前left

時,假設是從2移動到0:

//3張圖檔總寬
var totalWidth = 3 * igmWidth;
distance = -(totalWidth - Math.abs(newLeft - marginLeft));
           

接下來分析圖檔向右移動的情況

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

目标left > 目前left

時,這時候移動的就是正數了

目标left < 目前left

設定動畫效果

好的,既然邏輯清楚了,接下來就是愉快的寫代碼環節。

/**
 * 切換到某一個圖檔的索引
 * @param {*} index 要切換到目标的圖檔的索引
 * @param {*} direction 圖檔移動的方向 "left" "right"
 */
function switchTo(index, direction) {
    if (index == config.currentIndex) {
        return;
    }
    if (!direction) {
        direction = "left";
    }
    //最終的marginLeft
    var newLeft = (-index - 1) * config.imgWidth;
    animateSwitch();
    //重新設定目前索引
    config.currentIndex = index;
    //設定小圓點的選中狀态
    setDotsActiveStatus();

    /**
     * 逐漸改變marginLeft
     */
    function animateSwitch() {
        //先停止之前的動畫
        stopAnimate();
        //1、計算運動的次數
        var number = Math.ceil(config.timer.total / config.timer.duration);
        //目前的運動次數
        var currentNum = 0;

        //2、計算需要移動總距離
        var distance;
        //擷取目前的left值
        var marginLeft = parseFloat(getComputedStyle(config.doms.divImgs).marginLeft);
        var totalWidth = config.imgsNum * config.imgWidth;
        if (direction == "left") {
            if (newLeft < marginLeft) {
                distance = newLeft - marginLeft;
            } else {
                distance = -(totalWidth - Math.abs(newLeft - marginLeft));
            }
        } else {
            if (newLeft > marginLeft) {
                distance = newLeft - marginLeft;
            } else {
                distance = totalWidth - Math.abs(newLeft - marginLeft);
            }
        }

        //3、計算每次運動的距離
        var everyDistance = distance / number;

        //将函數注冊為config.timer.duration秒後調用,之後每隔config.timer.duration之後重複調用
        config.timer.id = setInterval(function () {
            //改變div的marginLeft
            marginLeft += everyDistance;
            if (direction == "left" && Math.abs(marginLeft) > totalWidth) {
                marginLeft += totalWidth;
            } else if (direction == "right" && Math.abs(marginLeft) < config.imgWidth) {
                marginLeft -= totalWidth;
            }

            config.doms.divImgs.style.marginLeft = marginLeft + "px";

            currentNum++;
            if (currentNum == number) {
                stopAnimate();
            }
        }, config.timer.duration)

        function stopAnimate() {
            clearInterval(config.timer.id);
            config.timer.id = null;
        }
    }
}
           

這裡我定義了一個函數去處理切換圖檔的邏輯,裡面的

animateSwitch

函數就是處理切換動畫的,注意首先要停止目前動畫,要不然一直點選的時候,就會一直執行這個函數,導緻速度越來越快。

之後就是我們前面分析過的邏輯,計算出移動的總距離,再算出每次運動的距離,通過這個距離再調用

setInterval

計時器,需要判斷的是當圖檔移動到最後一張時怎麼重新回到第一張,這裡我們把

marginLeft

的絕對值減去總寬度。

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

當到達

marginLeft = totalWidth

這個臨界值時,說明圖檔到了最後一張或者第一張,這個時候判斷一下圖檔的運動方向,如果圖檔向左運動,下一次重新整理假如到了下圖位置:

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

這個時候令

marginLeft = marfinLeft + totalWidth

就會運動到如下圖位置,當然這個在浏覽器中重新整理是非常快的,人眼是感覺不到已經變化的了

原生JS實作無縫輪播什麼叫無縫輪播無縫輪播的實作附JS代碼

圖檔向右運動同理,隻需要将

marginLeft = marfinLeft - totalWidth

即可

最後要取消動畫的時候,隻需要把計時器清除,并将id設為null。

以上就是無縫輪播的核心代碼以及邏輯分析,僅供參考,各位小夥伴們如果看懂了一定要自己動手練習,我會把整個js代碼和整個項目都放在末尾,有需要的可以看一下,也可以私信我發給你。前端知識多而亂,隻有掌握好基礎後期才能遊刃有餘。

附JS代碼

//輪播配置對象
var config = {
    imgWidth: 640, //圖檔寬度
    dotsWidth: 8, //圓點寬度
    doms: { //dom對象
        divImgs: document.querySelector('.img'),
        divDots: document.querySelector('.dots'),
        divArrow: document.querySelector('.arrow'),
        divLeft: document.querySelector('.left'),
        divRight: document.querySelector('.right'),
    },
    currentIndex: 0, //目前顯示的圖檔索引 0 ~ imgsNum-1
    timer: { //運動計數器配置
        duration: 16, //運動間隔的時間
        total: 500, //運動的總時間,機關毫秒
        id: null //計時器的id
    },
    autoTimer: null //自動移動的計時器
};

//圖檔數量
config.imgsNum = config.doms.divImgs.children.length;

/**
 * 初始化函數
 */
function init() {
    //初始化元素尺寸
    initSize();
    //初始化元素
    initElements();
    //初始化顯示圖檔的位置
    initImgPosition();
    //設定小圓點的選中狀态
    setDotsActiveStatus();
}
init();

/**
 * 初始化元素尺寸
 */
function initSize() {
    //包裹圓點的尺寸,+6是margin相加的值
    config.doms.divDots.style.width = (config.dotsWidth + 6) * config.imgsNum + "px";
    //輪播圖整體尺寸
    config.doms.divImgs.style.width = config.imgWidth * (config.imgsNum + 2) + "px";
}

/**
 * 初始化元素
 */
function initElements() {
    //建立小圓點
    for (var i = 0; i < config.imgsNum; i++) {
        var span = document.createElement('span');
        config.doms.divDots.appendChild(span);
    }
    //複制圖檔,實作無縫輪播
    var children = config.doms.divImgs.children;
    //第一張圖檔
    var first = children[0];
    //最後一張圖檔
    var last = children[children.length - 1];
    var newImg = first.cloneNode(true); //深度克隆
    config.doms.divImgs.appendChild(newImg);
    newImg = last.cloneNode(true); //深度克隆
    config.doms.divImgs.insertBefore(newImg, first);
}

/**
 * 初始化顯示圖檔的位置
 */
function initImgPosition() {
    //設定的currentIndex為幾就顯示第幾張圖檔
    var left = (-config.currentIndex - 1) * config.imgWidth;
    config.doms.divImgs.style.marginLeft = left + "px";
}

/**
 * 設定小圓點的選中狀态
 */
function setDotsActiveStatus() {
    //小圓點外邊框
    var children = config.doms.divDots.children;
    for (var i = 0; i < children.length; i++) {
        var dot = children[i];
        if (i == config.currentIndex) {
            dot.className = "active";
        } else {
            dot.className = "";
        }
    }
}


/**
 * 切換到某一個圖檔的索引
 * @param {*} index 要切換到目标的圖檔的索引
 * @param {*} direction 圖檔移動的方向 "left" "right"
 */
function switchTo(index, direction) {
    if (index == config.currentIndex) {
        return;
    }
    if (!direction) {
        direction = "left";
    }
    //最終的marginLeft
    var newLeft = (-index - 1) * config.imgWidth;
    animateSwitch();
    //重新設定目前索引
    config.currentIndex = index;
    setDotsActiveStatus();

    /**
     * 逐漸改變marginLeft
     */
    function animateSwitch() {
        //先停止之前的動畫
        stopAnimate();
        //1、計算運動的次數
        var number = Math.ceil(config.timer.total / config.timer.duration);
        //目前的運動次數
        var currentNum = 0;

        //2、計算需要移動總距離
        var distance;
        //目前的left值
        var marginLeft = parseFloat(getComputedStyle(config.doms.divImgs).marginLeft);
        var totalWidth = config.imgsNum * config.imgWidth;
        if (direction == "left") {
            if (newLeft < marginLeft) {
                distance = newLeft - marginLeft;
            } else {
                distance = -(totalWidth - Math.abs(newLeft - marginLeft));
            }
        } else {
            if (newLeft > marginLeft) {
                distance = newLeft - marginLeft;
            } else {
                distance = totalWidth - Math.abs(newLeft - marginLeft);
            }
        }

        //3、計算每次運動的距離
        var everyDistance = distance / number;

        //将函數注冊為config.timer.duration秒後調用,之後每隔config.timer.duration之後重複調用
        config.timer.id = setInterval(function () {
            //改變div的marginLeft
            marginLeft += everyDistance;
            if (direction == "left" && Math.abs(marginLeft) > totalWidth) {
                marginLeft += totalWidth;
            } else if (direction == "right" && Math.abs(marginLeft) < config.imgWidth) {
                marginLeft -= totalWidth;
            }

            config.doms.divImgs.style.marginLeft = marginLeft + "px";

            currentNum++;
            if (currentNum == number) {
                stopAnimate();
            }
        }, config.timer.duration)

        function stopAnimate() {
            clearInterval(config.timer.id);
            config.timer.id = null;
        }
    }
}

/**
 * 點選左右按鈕和小圓點切換圖檔
 */
function changeImg() {
    //給按鈕注冊點選事件
    config.doms.divArrow.onclick = function (e) {
        if (e.target.classList.contains("left")) {
            var index = config.currentIndex - 1;
            if (index < 0) {
                index = config.imgsNum - 1;
            }
            switchTo(index, "right");
        } else {
            var index = (config.currentIndex + 1) % config.imgsNum;
            switchTo(index, "left");
        }
    }

    //給小圓點注冊點選事件
    config.doms.divDots.onclick = function (e) {
        if (e.target.tagName == "SPAN") {
            var index = Array.from(this.children).indexOf(e.target);
            switchTo(index, index > config.currentIndex ? "left" : "right");
        }
    }
}

//點選左右按鈕切換圖檔
changeImg();

/**
 * 自動輪播
 */
config.autoTimer = setInterval(function() {
    var index = (config.currentIndex + 1) % config.imgsNum;
    switchTo(index, "left");
}, 3000);
           

整個項目zip

百度網盤連結:https://pan.baidu.com/s/1vQB8t_dI1zZeeXa-TA9rzw

提取碼:6ie9