網易音樂版輪播-react元件版本

也還不夠黑丶發表於2018-02-24

說明:

此版本輪播圖為仿照網易雲音樂PC播放器上首頁的輪播。

網易的輪播特殊的地方就在於,如果你滑動不相鄰的兩張圖片,其過渡效果並不是滑動過渡,而是一個跳出過渡,此方面原理與最開始設計輪播排版時候有極大關聯。

此輪播為純react環境下的es6寫法,通過對state中陣列的重組排列,配合樣式。達到輪播的效果

無任何依賴,最終效果為封裝成react元件開放介面併發布出去

注:此文章為正推,並在開發完成後進行總結優化。

一、搭建架構

此方面的文章應該很多,我就不必過多介紹,去github上找個react腳手架搭建一下基本專案框架即可。

我用的框架為改良過的一版本dva框架。react腳手架,github上很多,推薦自己選擇一款進行改良,我用的並不一定適合你。

二、準備材料

1.大於4張尺寸相同圖片。(本人為八張圖片命名1-8)
2.react環境
3.網易雲PC播放器

三、開發

先把首張圖片和左右兩側能看見的圖片位置擺好,
最開始的靜態結構是這個樣子的
新手注意:如發現程式碼刺眼,less、es6語法自行惡補

import React from `react`;

import styles from `./Slide.less`;

class Slide extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      dir: [
        { name: `middle` },
        { name: `start` },
        { name: `normal` },
        { name: `normal` },
        { name: `normal` },
        { name: `normal` },
        { name: `normal` },
        { name: `end` },
      ],
    };
  }
  render() {
    const { dir } = this.state;
    return (
      <div key={key} className={styles.root}>
        {/* 外部容器*/}
        <div className={styles.slideBox}>
          {/* 內部迴圈*/}
          {
            dir.map((item, key) => {
              return (
                <div className={`${styles.slide} ${styles[item.name]}`}> // 此處偷懶
                  <img src={`./images/${key + 1}.png`} // 此處偷懶 alt="./images/404.png" />
                  <div // 蒙板
                    className={styles.masking}
                  >{``}</div>
                </div>
              );
            })
          }
        </div>
      </div>
    );
  }
}

export default Slide;

less如下

.root{
  width: 100%;
  background: #ccc;
  .slideBox{
    width: 50%;
    height: 15vw;
    margin: 0 auto;
    position: relative;
    background: #ccc;
    .slide{
      position: absolute;
      img{
        width: 100%;
      }
      .masking{ // 蒙板,有個灰度漸變的效果
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, .15);
      }
      &.middle{ // 此為中間展示的那張
        left: 10%;
        bottom: 0%;
        width: 80%;
        z-index: 33;
      }
      &.start{ // 第一張則為左側那張
        left: 0%;
        bottom: 0%;
        width: 75%;
        z-index: 22;
        }
      &.end{ // 最後一張及右側那張
        right: 0%;
        bottom: 0%;
        width: 75%;
        z-index: 22;
      }
      &.normal{ // 此為隱藏圖片的樣式
        left: 13%;
        bottom: 0%;
        width: 74%;
        z-index: 11;
      }
    }
  }
}

這樣最開始的位置就擺好了。

隱藏圖片的位置很重要,因為上面也說了,跨圖片滑動時需要改成跳出效果。大概在如下位置,展示的那張圖片完美的將其擋住。


接下來,進行事件新增。我們先不管輪播下方的一排導航點。先加上左右點選操作。

在蒙板層加上onClick操作,(也可以加在slide層) 如下:

<div
    className={item.name === `middle` ? `` : styles.masking}
    onClick={() => this.slide(item.name, key)}
>{``}</div>

點選圖片時的方法

slide(name, key) {  // 圖片點選邏輯
    // 記錄當前節點
    this.setState({ current: key });
    // 陣列操作方法
    this.imgArr(name);
  }

陣列操作方法

imgArr(name) { // 陣列處理
    let dirCopy = this.state.dir;
    if (name === `start`) {  // 點選左側那張
      const pop = dirCopy.pop(); // 從陣列尾部彈出一個元素
      dirCopy.unshift(pop); // 尾部元素新增到陣列頭部
    } else if (name === `end`) { // 點選右側那張
      const shift = dirCopy.shift(); // 從陣列頭部彈出一個元素
      dirCopy.push(shift);  // 新增到陣列尾部
    }
    this.setState({ dir: dirCopy }); // 儲存重新排列的陣列 並觸發render
}

過渡樣式新增

1.過渡樣式主要有旋轉時,蒙版層的漸變。
2.旋轉時平滑的定位過渡。
3.旋轉時層級的變化放在優化環節單獨講解。

.slide{
    ... , // 此處為原樣式保留的意思(下面都以此規則顯示)
    transition: all 0.3s ease-in-out;
    user-select: none; // 禁止使用者選中(防止圖片被選中時變色);
    &:hover{ // 滑鼠經過時顯示小手樣式
      cursor: pointer;
    }
}
.masking{
    ... ,
    transition: all 0.3s 0.2s linear;
}
&.middle{
    ... ,
    .masking{
      background: transparent;
    }
}

這個樣子的話點選圖片左右兩側時就可以初步旋轉起來了。

到此為止的步驟所完成的樣式輪播為最基礎的‘旋轉木馬es6版本’,有需要的朋友已經可以在以上程式碼中進行優化總結,放在自己的專案中去。

選單按鈕開發

動態的根據圖片的數量迴圈出選單按鈕的數量,程式碼跟圖片迴圈類似。

<div className={styles.slideBox}>
  ... ,
  {/* 導航按鈕*/}
  <div className={styles.point}>
    {
      this.state.dir.map((item, key) => { // 根據圖片數量進行迴圈
        return (
          <span
            key={key}
            className={item.name === `start` ? styles.hover : ``} // 給予當前顯示的按鈕樣式變化
            onMouseEnter={() => this.pointFunc(key - 1)} // 滑鼠進入動畫
          >{}</span>
        );
      })
    }
  </div>
</div>

樣式方面:

  .point{
    width: 100%;
    position: absolute;
    left: 0;
    bottom: -23px;
    z-index: 999;
    text-align: center;
    span{
      display: inline-block;
      width: 20px;
      height: 3px;
      background-color: #2E3033;
      margin-left: 9px;
      &.hover{
        background-color: #7F8082;
      }
      &:hover{
        cursor: pointer;
      }
    }
  }

滑鼠進入方法pointFunc();

  pointFunc(index) { // 按鈕點選
    const { current } = this.state;
    const dirCopy = this.state.dir;
    if (index < current) { // 滑鼠經過左側的按鈕
      for (let i = 0; i < (current - index); i += 1) { // 判斷距離
        const shift = dirCopy.shift(); // 進行陣列操作
        dirCopy.push(shift);
      }
    } else if (index > current) { // 滑鼠經過右側的按鈕
      for (let i = 0; i < (index - current); i += 1) {
        const pop = dirCopy.pop();
        dirCopy.unshift(pop);
      }
    }
    this.setState({ dir: dirCopy }); // 觸發react-render重新渲染頁面
    this.setState({ current: index }); // 記錄當前圖片節點
  }

加完按鈕圖片後效果如下:


這個時候,核心效果已經出來了,經過嚴謹的佈局和動畫調節後,最終達到了預期的網易播放器的特殊動畫效果。(滑鼠經過相鄰的圖片時為滑動,經過不相鄰圖片按鈕的時候改為跳動效果)。

程式碼效果


注:樣式方面還有需要優化的地方請自行調節


總結

在這個元件模式開發時代,如果你做的東西不能保留下來並且開放出去,我認為是一件可悲的事情。所以下一篇文章將把此react元件進行開放式處理,開放一些可調節介面、響應式處理,並且最後打包成npm包,以外掛的形式開放出去。

相關文章