從零開始寫一個輪播

什麼是輪播

輪播,英文slider,也就是幻燈片的意思,咱們能夠手動或者自動切換一張張的照片達到顯示的目的。各大電商網站都會使用輪播來展現商品,能夠在相對較小的空間裏,實現多種展現。css

怎麼實現一個輪播

通常,咱們能夠兩種方法來實現輪播,例如,核心思路使用透明度來切換照片,使用距離來平移照片。在這裏咱們使用平移的方式來實現輪播,並經過幾組核心數據來實現slide的切換,此外,通常還具備多種方法能夠實現無縫切換。html

需求分解

咱們實現的輪播,所須要具備的功能能夠分爲如下幾部分:app

  1. 點擊左右箭頭,先後切換slide
  2. 無縫切換輪播
  3. 點擊輪播中的導航按鈕,切換不一樣的slide
  4. 初始狀態會每隔3s自動切換slide
  5. 鼠標懸浮在slide上時,自動輪播中止,移出後會恢復自動輪播狀態
  6. 考慮複用性,抽象爲一個組件,其餘如點擊切換等功能考慮爲插件注入

1. 向前和向後切換和無縫切換

1. 結構和樣式

咱們設置兩個容器,經過在父容器使用transform進行左右平移,translateX()是相對於自身的寬度,而在子容器裏經過絕對定位來進行平移,其中left屬性相對的是父容器的寬度,這樣父容器平移100%,子容器距離左邊-100%,就能夠相互抵消。ide

HTML結構以下組件化

<div id="m-slider">
  <div class="slide">
    <img >
  </div>
  <div class="slide">
    <img >
  </div>
  <div class="slide">
    <img >
  </div>
</div>
<button id="prev">prev</button>
<button id="next">next</button>

CSS以下,略有刪減,能夠參考源碼網站

#m-slider {
  position: relative;
  width: 100%;
  height: 100%;
  transform: translateX(-100%);
}
.slide {
  position: absolute;
}
 .slide:nth-child(1) {
  left: 0%;
}
.slide:nth-child(2) {
  left: 100%;
}
.slide:nth-child(3) {
  left: 200%;
}

此時咱們知道能夠顯示第二張圖片。this

2. 先後邏輯控制

數據模型:url

  1. slideIndex爲slide的索引,取值爲0,1,2,表示始終只有三張圖片在HTML結構當中。
  2. offset表示當前距離最左邊切換圖片的數量,每次向後切換時,都會加1,取值範圍爲1~圖片的總數量

相關說明:.net

  1. normalIdx()方法是一個將數字轉換爲len範圍的方法,接收索引和長度,返回一個在len以內的數值,這裏的len固定爲3
  2. go()方法,接收1或者-1,分別表示向前和向後移動,以向前移動爲例,此時作了兩件事,相關因此會更新,改變容器的偏移,主要是offsetslideIndex會發生變化,都須要調用規範化方法,使其數值始終處於3以內。
  3. calcDistance()方法爲計算移動距離的方法,主要有父容器和slide的距離計算。
var slider = document.getElementById('m-slider')
var slides = document.querySelectorAll('.slide')
var prev = document.getElementById('prev');
var next = document.getElementById('next');
var slideIndex = 1; // one of 0,1,2
var offset = 1; // 顯示的slide距容器最左邊的偏移量

prev.onclick = function() {
    go(-1);
}
next.onclick = function() {
    go(1);
}
function normalIdx(index,len) {
  return (index + len) % len;
}
function go(foreward) {
  
  offset += foreward; // 向右移動了一個,全部偏移量也須要加一
  slideIndex += foreward; // 向右移動了一個,因此加一,須要歸三化
  slideIndex = normalIdx(slideIndex,3);
  var prevIndex =  normalIdx(slideIndex - 1,3);
  var nextIndex =  normalIdx(slideIndex + 1,3);
  calcDistance(prevIndex,slideIndex,nextIndex,offset);
}
function calcDistance(prevIndex,slideIndex,nextIndex,offset) {
  slider.style.transform = `translateX(${-100*offset}%)`
  //console.log(slider.style.transform)
  console.log(offset)
  // slide offseet
  slides[prevIndex].style.left = `${100*(offset-1)}%`;
  slides[slideIndex].style.left = `${100*offset}%`;
  slides[nextIndex].style.left = `${100*(offset+1)}%`;
}

2. 無縫輪播

每次切換時,slideleft屬性都會改變,父容器的transform也發生改變,相互抵消,輕鬆實現無縫切換,注意: 咱們這裏只設置了三張照片,恰好與要求一致,並無對相關數據進行計算處理。插件

/** 無論輪播的照片有多少,只有三欄是常駐的
 *      -------------------
 *      |     |     |     |  
 * -------------------------------
 * |    |     |     |     |      |
 * |    |     |     |     |      |
 * |    |  1  |  2  |  3  |      |
 * |    |     |     |     |      |
 * |    |     |     |     |      |
 * -------------------------------
 *      |     |     |     |
 *      -------------------
 */

3. 面向對象化

咱們將前面寫的業務代碼,用面向對象的思想來重構一下。

HTML發生一些改變,咱們將Img經過JS動態注入

<div id="m-slider">
  <div class="slide"> </div>
  <div class="slide"> </div>
  <div class="slide"> </div>
</div>
<button id="prev">prev</button>
<button id="next">next</button>

JS部分,這裏`pageNum表示圖片的總數量,能夠爲任意數字,renderImg()爲渲染圖片的方法,主要在切換的時候改變圖片的url,第一次須要進行初始化。

// Component
class Slider {
    constructor(el) {
      // omit some code
      this.offset = 1;// 偏移量,即個數
      this.slideIndex = 1;// slide index one of 0, 1, 2
      this.pageIndex = 1;// page index of total pictures
      this.pageNum = 6;// total pictures
  }
  // 標準化index,使index始終爲0,1,2中的一個
  normalIdx(index,len) {
    return (index + len) % len;
  }
  
  // slide move according to the flag
  go(flag) {
    // 1. offset and slideIndex change
    this.offset += flag;
    this.pageIndex += flag;
    const slideIndex = this.slideIndex = this.normalIdx( this.slideIndex += flag,3);
     
     // 計算移動距離
     this.calcDistance();
  }
  
  // style change
  calcDistance() {
      const offset = this.offset;
    const slideIndex = this.slideIndex;
    // index
    const prevIndex = this.normalIdx( slideIndex - 1, 3)
    const nextIndex = this.normalIdx( slideIndex + 1, 3)
    this.pageIndex = this.normalIdx(this.pageIndex,this.pageNum) 

    // 2. container and slide move
    this.container.style.transform = `translateX(${-100 * (offset)}%) `;
    this.slides[prevIndex].style.left = `${100 * (offset - 1)}%`;
    this.slides[slideIndex].style.left = `${100 * offset}%`;
    this.slides[nextIndex].style.left = `${100 * (offset + 1)}%`;
    //console.log(this.pageIndex,this.slideIndex)
    this.renderImg(this.pageIndex,this.slideIndex);
  }
  renderImg(pageIndex,slideIndex) {
      for(let i = -1; i <= 1; i++) {
      const index = (slideIndex+i+3) % 3; // 決定圖片的url和left漂移
      let img = this.slides[index].querySelector('img');
      let picId = this.normalIdx( pageIndex + i, this.pageNum) + 1;
      
      // 頁面初始化使用,運行一次
      if(!img) {
        img = document.createElement('img');
        this.slides[index].appendChild(img);
      }
      img.src = 'http://placehold.it/300x200&text=' + picId + '-Mint';
    }
  }
  start() {
      this.bindEvents()
    this.renderImg(1,1);
  }
}

const mySlider = new Slider('m-slider');
mySlider.start()

4. 完整版

Source

添加了nav功能,點擊對應的小按鈕,便可跳轉到指定的slide上

5. 後記

後期將考慮將輪播進行組件化,封裝成獨立的組件,添加更多可定製的功能。

相關文章
相關標籤/搜索