iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0

元件介紹

Carousel 是一個像旋轉木馬一樣會輪流轉的輪播元件。在一個內容空間有限的可視範圍中進行內容的輪播展示。通常適用於一組圖片或是卡片的輪播。

Carousel 的樣式也是五花八門,隨便在 google 下一個關鍵字就能夠找到各種不同形式的 Carousel。

所以在這邊我們也是挑一個簡單易做的 Carousel 來實現。

參考設計 & 屬性分析

dataSource

由於是一組輪流播放的圖片,所以首先我們需要提供給這個元件一個 list 的資料,其中 list 的每一筆資料我們希望能夠包含一個 image url。

autoplay

autoplay 提供一個 boolean 來決定這個輪播是否自動切換。

dots

dots 是一個 boolean,用來決定是否出現「指示點」。

介面設計

屬性 說明 類型 默認值
className 客製化樣式 string
dataSource 輪播資料 list of image url
autoplay 是否自動播放 boolean false
hasDots 是否顯示指示點 boolean true
hasControlArrow 是否顯示上一個、下一個切換鍵 boolean true

元件實作

我們的 Carousel 包含了輪播圖片本身、左右切換按鈕,以及指示點,所以 DOM 大致上會長得像下面這個形狀:

<CarouselWrapper>
  <ImageWrapper>
    {...images...}
  </ImageWrapper>
  <ControlButtons />
  <Dots />
</CarouselWrapper>

由下圖知道,輪播圖片、左右切換按鈕以及指示點都是疊在一起的,有點是分不同圖層的概念,所以這邊的定位都是使用 position: absolute;

圖片輪播的計算方法

我們就直接來看 Carousel 的核心,到底要怎麼讓圖片可以輪播。
這邊我們是要做 slide 動畫的輪播,所以為了讓圖片可以在 X 軸上左右滑動,我們勢必會使用到 position: absolute;lefttransition 這三個關鍵的 CSS。

如下圖示意,為了讓圖片能夠左右滑動輪播,我們可以先將圖片排成一整排,並且在可視範圍之外的地方隱藏這些圖片。

排成一整排的方式不能像我們以前那樣使用 flex 佈局來達成,而是會需要算一些數學,計算出每一張圖片的位置,也就是他的 left 值,之後要輪轉的時候,就是去改變這個 left 數值,搭配 transition 過場動畫,就能夠做到我們輪播的效果。

概念上已經說明完了,接下來我們來看程式碼,left 的計算很簡單,就是目前迭代到的這個 image 與正在可視範圍中的那個 current image 的距離,乘上圖片的寬度就是了:

const makePosition = ({ itemIndex }) => (itemIndex - currentIndex) * imageWidth;

<ImageWrapper>
  {
    dataSource.map((imageUrl, index) => (
      <Image
        key={imageUrl}
        src={imageUrl}
        alt=""
        $left={makePosition({ itemIndex: index })}
      />
    ))
  }
</ImageWrapper>

切換圖片的左右按鈕

切換左右的按鈕我希望只專注在計算哪一張圖片是 current ,也就是正在可視範圍中的圖片,避免在這些 function 裡面做太多其他的事,保持他功能的單純性。

所以看下面 click next 的 function,我只有計算讓 current index 不斷的 +1,直到尾部的時候再從頭開始;反之,click prev 的 function 就是讓 current index 不斷的 -1,直到 0 的時候再從尾部開始:

const getIndexes = () => {
  const prevIndex = currentIndex - 1 < 0 ? dataSource.length - 1 : currentIndex - 1;
  const nextIndex = (currentIndex + 1) % dataSource.length;

  return {
    prevIndex, nextIndex,
  };
};

const handleClickPrev = () => {
  const { prevIndex } = getIndexes();
  setCurrentIndex(prevIndex);
};

const handleClickNext = useCallback(() => {
  const { nextIndex } = getIndexes();
  setCurrentIndex(nextIndex);
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentIndex]);


<ControlButtons>
  <ArrowLeft onClick={handleClickPrev} />
  <ArrowRight onClick={handleClickNext} />
</ControlButtons>

自動播放

自動播放當然就是要靠我們的 setInterval 了,我們每 3 秒就改變 current index 一次,改變的方式我們就直接呼叫上面做好的 handleClickNext function 就可以了:

useEffect(() => {
  let intervalId;
  if (autoplay) {
    intervalId = setInterval(() => {
      handleClickNext();
    }, 3000);
  }
  return () => {
    clearInterval(intervalId);
  };
}, [autoplay, handleClickNext]);

以上就是我們 Carousel 的重點整理啦!簡單展示一下成果:


Carousel 元件原始碼:
Source code

Storybook:
Carousel


上一篇
【Day14】數據展示元件 - Card
下一篇
【Day16】數據展示元件 - Table
系列文
30 天擁有一套自己手刻的 React UI 元件庫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言