使用純JavaScript實現輪播功能-基礎

陳智翔
9 min readMar 16, 2023

--

輪播,常被用來展示商品或圖片,在各種網頁中常常可以看到,而在網路上有許多可以實現輪播的套件,如swiperJSOwl Carousel等。

而本文將告訴各位如何只使用純JavaScript,不使用其他外部套件來實現輪播功能。

概念:

在之前的工作中我常使用的輪播套件為slick.js,因此這次將藉由slick.js切入來了解輪播是如何實現的。

使用套件前的html:

使用套件後的html:

經由觀察由slick.js所構建的輪播的html代碼後,可以從中得出以下架構:

套件在外框(圖上方黑色虛線處)與內容物之間加入了一個容器,並將內容物放置其中。外框的區域為我們可視的範圍,而容器被置於外框之下,超出外框範圍的內容物都被隱藏。

並且當我們切換輪播的內容物時,容器往左移動,使可視範圍內的內容物改變,以此達成輪播的效果。

實作:

HTML

<div class="carousel">
<div class="container">
<div class="content red">1</div>
<div class="content yellow">2</div>
<div class="content blue">3</div>
<div class="content purple">4</div>
</div>
</div>
<div class="btnContainer">
<button class="btn btn-prev">&larr;</button>
<button class="btn btn-next">&rarr;</button>
</div>

CSS

body {
background-color: gray;
}

.carousel {
width: 500px;
margin: 0 auto;
border: 2px dashed #000;
background-color: #fff;
}
.content {
height: 100px;
color: #fff;
font-size: 36px;
font-weight: bold;
line-height: 100px;
text-align: center;
}

.red {
background-color: red;
}
.yellow {
background-color: rgb(209, 209, 0);
}
.blue {
background-color: blue;
}
.purple {
background-color: purple;
}

.btnContainer {
margin: 10px auto;
display: flex;
justify-content: center;
gap: 10px;
}
.btn {
width: 80px;
height: 40px;
font-size: 30px;
}

css需要注意的部分是外框(.carousel)需設置寬度,而內容物(.content)需要設置高度,輪播整體的高度需要由內容物的高度來支撐,內容物寬度的部分則不需要設置,稍後會透過js部分來控制輪播的可視物品數。

輪播物品的間距可以透過在.content下增加margin-right達成(須為px)。

JS

// 可調整參數
// 輪播內容物的顯示數量
const contentToShow = 2;
// 輪播切換時的速度,單位為ms
const moveSpeed = 500;

// 選取會使用的element
const carousel = document.querySelector(".carousel");
const container = document.querySelector(".container");
const allContent = document.querySelectorAll(".content");
const content = document.querySelector(".content");
const prevBtn = document.querySelector(".btn-prev");
const nextBtn = document.querySelector(".btn-next");
const contentComputeStyle = getComputedStyle(content);

// 取得輪播內容物個數
const contentAmount = document.querySelectorAll(".content").length;

let distanceBetweenContent;

// 輪播容器之位置
let position = 0;

// 全域變數,管理輪播是否可以切換
let disableMove;

// 設定輪播所需的style,也可以在css中直接新增
carousel.style.overflow = "hidden";
carousel.style.position = "relative";
container.style.display = "flex";
container.style.position = "absolute";

// 設定輪播切換的動畫時間
container.style.transition = `transform ${moveSpeed}ms`;

const setContentWidth = function () {
const carouselWidth = carousel.offsetWidth;

// 可藉由給予輪播內容物margin-right屬性來設定內容物間的間隔
const gap = parseInt(contentComputeStyle["margin-right"]);

// 基於內容物的顯示數量,計算各內容物所需的大小
const contentWidth = (carouselWidth - gap * Math.ceil(contentToShow - 1)) / contentToShow;

allContent.forEach((el) => (el.style.width = `${contentWidth}px`));

// 設定完內容物寬度後
// 設定內容物間x軸之差,此為容器移動1單位之距離
distanceBetweenContent = content.nextElementSibling.offsetLeft - content.offsetLeft;
};
const setContentHeight = function () {
// 基於內容物的高度來設定容器高度
carousel.style.height = contentComputeStyle.height;
};

const move = function (step) {
// 由於不斷切換輪播時會產生動畫不平均的現象,因此設定在動畫完成後,才可以繼續切換輪播
if (disableMove) return;
// 避免超出範圍的guard clauses
// contentAmount - contentToShow : 可允許可視範圍的最大值
if (-(position - step) > contentAmount - contentToShow || position - step > 0) return;

// 更新位置
position -= step;

// 移動
container.style.transform = `translateX(${distanceBetweenContent * position}px`;
};

// 動畫開始時,禁止移動,直到動畫結束
container.addEventListener("transitionstart", () => {
disableMove = true;
});
container.addEventListener("transitionend", () => {
disableMove = false;
});

prevBtn.addEventListener("click", function () {
move(-1);
});
nextBtn.addEventListener("click", function () {
move(1);
});

setContentWidth();
setContentHeight();

輪播的可視數量可以透過contentToShow參數進行調整,播放速度可以透過moveSpeed調整。

運作概念如下:

使用offsetLeft取得兩物品之差做為一單位的移動距離(不直接取物品寬度的原因是為可透過設置物品的margin-right來設定物品間距)。

之後將容器依照1單位的距離,使用transformX向左移動,並更新當前位置(position)。

整體程式如下:

https://codesandbox.io/s/sparkling-cookies-xe2yjb?file=/src/index.css

接下來會跟大家講解一些進階的拓展,如何達成無縫的循環撥放與RWD設置。

--

--