基於 flex 的 order 實現 carousel 輪播圖

Eyas發表於2018-06-13

原因

專案裡需要使用輪播圖,electron + vue 技術棧,專案應用一旦啟動會持續執行24小時,並且機器效能較差,所以很關注兩個點

  1. 記憶體洩漏
  2. 效能

目前社群的輪播元件,大多隻是適用於常規 web 應用,經過內部測試後,並不能滿足記憶體和效能方面的要求,所以需要自己實現輪播元件

思路

最開始找到了這篇文章,裡面講解了傳統的輪播圖實現思路和作者原創的輪播思路,並在文末給出了效能較高的原創方案。

作者的原創方案效能是很高了,但是我注意到每次執行輪播都需要移動一個 DOM 節點,這會觸發瀏覽器重排重繪,效能依舊不夠高,還可以繼續優化。

首先想到了 flex 佈局的 order 屬性:https://developer.mozilla.org/zh-CN/docs/Web/CSS/order

相容性

可以看到只有現代瀏覽器才支援,如果要相容老久瀏覽器就不用考慮本方案了,我的環境是electron 2.0,整合的chrome 61,可使用。

實現方案

本文章只記錄實現方案與虛擬碼,不會給出 demo。

基本功能實現

html結構

<div class="carousel">
  <div class="carousel-container" style="transition-duration: 0ms; transform: translate3d(0px, 0px, 0px);">
    <!-- 輪播列表元素 -->
    <div class="carousel-item" style="order: 0;"></div>
    <div class="carousel-item" style="order: 1;"></div>
    <div class="carousel-item" style="order: 2;"></div>
    <div class="carousel-item" style="order: 3;"></div>
    <div class="carousel-item" style="order: 4;"></div>
  </div>
</div>
<style>
  .carousel {
    width: 100%;
  }
  .carousel-container {
    width: 100%;
    display: flex;
    transition-property: transform;
  }
  .carousel-item {
    width: 100%;
  }
</style>
複製程式碼

從裡面元素開始解釋

  1. 父級設定 display: flex ,子級可以通過 order 屬性實現排序,這種排序雖然依然會引發重排和重繪,但是開銷更小
  2. 外圍一層元素,使用 transition 實現 動畫,使用 transform 的 translate3d 實現硬體加速與顯示範圍。在非動畫狀態,X軸的位置永遠都是 0,在動畫狀態,才給 X軸 賦值,所以整個元件其實就是在做兩件事: 順序X軸位置(也就是動畫)
  3. 順序:非動畫狀態需要 X 軸一直為0,那麼就要保證當前要顯示的輪播元素的 order 值最小,我暫時約定最小為 0,因為動畫涉及到下一張,所以當前輪播的order 為 0,下一張為 1,其他的只要大於1 即可。
  4. 動畫,如果需求是切換的時候不需要動畫,那麼保證順序就已經完成了輪播切換了,但需求通常需要動畫。動畫的實現由三部分,起始狀態結束狀態重置狀態
  5. 起始狀態:動畫一開始,就是要在當前輪播元素開始,對應的X軸是0,起就是靜止狀態,所以起止狀態不需要設定,預設就是了,所以通常其實狀態無需處理
  6. 結束狀態:結束的狀態是下一張輪播元素完全顯示,也就是X增加一個 輪播元素的寬度。動畫時間 transition-duration 賦值 500ms,就能實現動畫。
  7. 重置狀態:動畫完成後,重新計算各個元素的 order 值,把 X 軸重設為0,動畫時間重設為0

到此就完成了輪播元件的基本功能

功能擴充套件

自動輪播

先實現一個函式 next() 方法,定時呼叫

拖動滾動

  1. 記錄開始拖動時滑鼠位置的 X軸
  2. 移動過程中獲取滑鼠位置X軸,減去開始拖動時的X軸位置,得到X軸移動的距離,再把這個距離數字賦值給 translate3d 的X軸

反向動畫與拖動

通常的輪播都是 從右往左 滾動的,但是有時需要相容 從左往右,實現方案:

非動畫狀態無需調整,主要關注動畫狀態。

  1. 排序:要反過來排序,當前顯示的元素 order 為0,下一張為 -1,其他的小於 -1即可
  2. 動畫的不同狀態都需要調整
  3. 起始狀態:X軸位置:-1 * (輪播條數 - 1) * 輪播寬度
  4. 結束狀態:X軸位置:-1 * (輪播條數 - 與上條間隔數量) * 輪播寬度
  5. 重置狀態:X軸位置:0,排序重置為正向

反向拖動,如果拖動的時候拖動的距離是個正數,則馬上更新順序為反向,如果為負數,馬上更新順序為正向

總結

該方案的效能很高,但是相容性不太好。而且實現過程中,對元素的排序計算如果涉及到反向動畫的話會比較複雜

相關文章