使用 React 實現一個輪播組件

vjsc7136 8年前發布 | 53K 次閱讀 React JavaScript開發

來自: http://qiutc.me/post/使用-React-實現一個輪播組件.html

tip

React 剛出來不久,組件還比較少,不像 jquery 那樣已經有很多現成的插件了,當是就自己寫了一個基于 React 的輪播組件,當然只是一個小 demo,剛剛有用 es6 的語法重新改了改,就想著寫一個小教程給新手,如何實現一個 React 的小組件。

先放上倉庫地址,可以先 clone 來看看代碼: https://github.com/TongchengQiu/react-slider

react-slider 是一個圖片輪播的組件,支持的配置有 圖片(必須好不好,要不然輪播毛)、輪播圖片的速度、是否自動輪播、自動輪播的時候鼠標放上去是否暫停、自動輪播速度、是否需要前后箭頭、是否需要 dot (我不知道怎么表述好,反正意思你懂)。

第一步,需求

首先,寫一個組件必須先考慮改組件的需求有哪些,支持的配置需要哪些。如上已經說了改組件的需求:

  • 輪播的圖片
  • 配置輪播圖片切換的速度
  • 可配置是否自動輪播
  • 可配置自動輪播的時候鼠標放上去是否暫停
  • 可配置自動輪播的速度
  • 可配置是否需要前后箭頭
  • 可配置是否需要 dot (我不知道怎么表述好,反正意思你懂)
    這一步先到此為止~~~

第二步,構建項目

這里我們是使用 React 框架,當然也是用它的好搭檔 webpack 來構建自動化流程咯~:smirk:

不懂 webpack 的配置可以看我的博客關于 webpack 使用的文章,謝謝~:smile:

這是項目開發目錄的文件結構:

src
├── Slider              #Slider組件
| |
| ├──SliderItem 
| | ├──SliderItem.jsx
| | └──SliderItem.scss
| |
| ├──SliderDots 
| | ├──SliderItem.jsx
| | └──SliderItem.scss
| |
| ├──SliderArrows 
| | ├──SliderItem.jsx
| | └──SliderItem.scss
| |
| ├──Slider.jsx 
| |
| └──Slider.scss
|
├──images              #存放demo用的圖片文件
|
└── index.js #demo的入口文件

</div>

看目錄結構我們應該明白了,我們主要關注 Slider 文件夾。

第三步,基于需求的開發

這里我們開發組件的模式是按照需求驅動型,回到第一步的需求,我們先不管組件內代碼,先寫出我們想怎么樣配置使用組件:

// index.js
import React from 'react';
import { render } from 'react-dom';
import Slider from './Slider/Slider';

const IMAGE_DATA = [
  {
    src: require('./images/demo1.jpg'),
    alt: 'images-1',
  },
  {
    src: require('./images/demo2.jpg'),
    alt: 'images-2',
  },
  {
    src: require('./images/demo3.jpg'),
    alt: 'images-3',
  },
];

render(
  <Slider
    items={IMAGE_DATA}
    speed={1.2}
    delay={2.1}
    pause={true}
    autoplay={true}
    dots={true}
    arrows={true}
  />,
  document.getElementById('root')
);

</div>

可以看到,在使用 Slider 組件的時候,根據需求,我們可以傳入這些屬性來配置組件。

一個 items 數組,決定了需要輪播的內容,items 里面每個元素都是一個對象,有 src 和 alt 屬性,分別是輪播圖片的 src 地址和 alt 內容;

speed 是圖片切換的時候的速度時間,需要配置一個 number 類型的數據,決定時間是幾秒;

autoplay 是配置是否需要自動輪播,是一個布爾值;

delay 是在需要自動輪播的時候,每張圖片停留的時間,一個 number 值;

pause 是在需要自動輪播的時候,鼠標停留在圖片上,是否暫停輪播,是一個布爾值;

dots 是配置是否需要輪播下面的小點;

arrows 是配置是否需要輪播的前后箭頭;

第四步,編寫組件

首先我們來考慮一下需要多少個組件,最外層的 Slider 組件是毋庸置疑的了。

根據需求我們可以分出一個 SliderItem 組件,一個 SliderDots,一個 SliderArrows 組件,分別是 輪播每個圖片的item,輪播下面dots的組件,左右箭頭組件。

我們先來編寫每個小組件,組件是對外封閉獨立的,所以它只需要對外暴漏屬性的配置即可。

SliderItem

因為 SliderItem 是展示輪播圖片的每個內容的,所以它需要的屬性有,src 和 alt,因為之前我們已經把每個輪播項寫成一個對象了,所以把 src 和 alt 作為屬性放在 item,我們只需要一個 item 屬性即可;它還需要決定它在父組件中大小,因為在未使用組件的時候我們是不知道輪播項的數目的,所以它的寬度需要根據父組件傳給它count(圖片數目)來計算。所以它需要的屬性有:

  • item (有 src 和 alt 屬性)
  • count (輪播項總數目,計算每個輪播項的寬度)
import React, { Component } from 'react';

export default class SliderItem extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    let { count, item } = this.props;
    let width = 100 / count + '%';
    return (
      <li className="slider-item" style={{width: width}}>
        <img src={item.src} alt={item.alt} />
      </li>
    );
  }
}

</div>

let width = 100 / count + '%'; 即是計算它占父組件的寬度百分比。

SliderDots

對于 SliderDots 組件,我們需要一個 count(輪播項總數目)來決定顯示幾個 dot,還需要一個 nowLocal 屬性來判斷哪個 dot 對應當前顯示的輪播項,點擊每個 dot 的是否需要一個回調函數 turn 來做出響應。所以它需要的屬性有:

  • count(輪播項總數目)
  • nowLocal(當前的輪播項)
  • turn(點擊 dot 回調函數)
import React, { Component } from 'react';

export default class SliderDots extends Component {
  constructor(props) {
    super(props);
  }

  handleDotClick(i) {
    var option = i - this.props.nowLocal;
    this.props.turn(option);
  }

  render() {
    let dotNodes = [];
    let { count, nowLocal } = this.props;
    for(let i = 0; i < count; i++) {
      dotNodes[i] = (
        <span
          key={'dot' + i}
          className={"slider-dot" + (i === this.props.nowLocal?" slider-dot-selected":"")}
          onClick={this.handleDotClick.bind(this, i)}>
        </span>
      );
    }
    return (
      <div className="slider-dots-wrap">
        {dotNodes}
      </div>
    );
  }
}

</div>

代碼可以看出,我們用 for 循環根據 count 來決定了需要多少個 dot,然后在每個 dot 綁定函數傳入一個 i 值,并且如果這個 dot 對于當前顯示的輪播項,就多加一個class slider-dot-selected。每個 dot click 綁定的函數還需要計算需要向前或者向后移動多少個輪播項,然后回調 turn 函數。

SliderArrows

對于 SliderArrows 組件,我們只需要一個 turn 函數作出回調。

廢話不多少,代碼如下:

import React, { Component } from 'react';

export default class SliderArrows extends Component {
  constructor(props) {
    super(props);
  }

  handleArrowClick(option) {
    this.props.turn(option);
  }

  render() {
    return (
      <div className="slider-arrows-wrap">
        <span
          className="slider-arrow slider-arrow-left"
          onClick={this.handleArrowClick.bind(this, -1)}>
          <
        </span>
        <span
          className="slider-arrow slider-arrow-right"
          onClick={this.handleArrowClick.bind(this, 1)}>
          >
        </span>
      </div>
    );
  }
}

</div>

這個組件下面有兩個箭頭,分別是向前和向后,回調的 turn 是 1 和 -1。

Slider 組件

import React, { Component } from 'react';

require('./Slider.scss');

import SliderItem from './SliderItem/SliderItem';
import SliderDots from './SliderDots/SliderDots';
import SliderArrows from './SliderArrows/SliderArrows';

export default class Slider extends Component {
  constructor(props) {
    super(props);
    this.state = {
      nowLocal: 0,
    };
  }

  // 向前向后多少
  turn(n) {
    var _n = this.state.nowLocal + n;
    if(_n < 0) {
      _n = _n + this.props.items.length;
    }
    if(_n >= this.props.items.length) {
      _n = _n - this.props.items.length;
    }
    this.setState({nowLocal: _n});
  }

  // 開始自動輪播
  goPlay() {
    if(this.props.autoplay) {
      this.autoPlayFlag = setInterval(() => {
        this.turn(1);
      }, this.props.delay * 1000);
    }
  }

  // 暫停自動輪播
  pausePlay() {
    clearInterval(this.autoPlayFlag);
  }

  componentDidMount() {
    this.goPlay();
  }

  render() {
    let count = this.props.items.length;

    let itemNodes = this.props.items.map((item, idx) => {
      return <SliderItem item={item} count={count} key={'item' + idx} />;
    });

    let arrowsNode = <SliderArrows turn={this.turn.bind(this)}/>;

    let dotsNode = <SliderDots turn={this.turn.bind(this)} count={count} nowLocal={this.state.nowLocal} />;

    return (
      <div
        className="slider"
        onMouseOver={this.props.pause?this.pausePlay.bind(this):null} onMouseOut={this.props.pause?this.goPlay.bind(this):null}>
          <ul style={{
              left: -100 * this.state.nowLocal + "%",
              transitionDuration: this.props.speed + "s",
              width: this.props.items.length * 100 + "%"
            }}>
              {itemNodes}
          </ul>
          {this.props.arrows?arrowsNode:null}
          {this.props.dots?dotsNode:null}
        </div>
      );
  }
}

Slider.defaultProps = {
  speed: 1,
  delay: 2,
  pause: true,
  autoplay: true,
  dots: true,
  arrows: true,
  items: [],
};
Slider.autoPlayFlag = null;

</div>

  • 在這里我們先是 import 依賴,以及 需要的 子組件。
  • Slider 有一個狀態 nowLocal,是表明當前輪播的第幾項。
  • 前面一直講 turn 函數,我們就先分析一下這個函數:
    turn(n) {
      console.log();
      var _n = this.state.nowLocal + n;
      if(_n < 0) {
        _n = _n + this.props.items.length;
      }
      if(_n >= this.props.items.length) {
        _n = _n - this.props.items.length;
      }
      this.setState({nowLocal: _n});
    }

它傳入一個 參數 n ,決定向前或者向后移動多少個輪播項,向前和向后分別對于 - 和 +。

turn 函數內,聲明一個 _n 變量表示下一個輪播的第幾項。

如果 _n 小于 0 ,那當然是不行的,所以就會讓 _n 變成最后一項;

如果 _n 大于 items 的長度 ,那當然也是不行的,所以就會讓 _n 變成第一項;

最后改變狀態 nowLocal 等于 _n 。

  • 下面是一個開始自動輪播的函數 goPlay:
    goPlay() {
      if(this.props.autoplay) {
        this.autoPlayFlag = setInterval(() => {
          this.turn(1);
        }, this.props.delay * 1000);
      }
    }

如果 autoplay 是 true,則執行 setInterval 來自動調用 this.turn(1) 向前移動輪播項, this.props.delay * 1000 就是根據配置的 delay 來決定多久移動一次。

這里我們需要一個 autoPlayFlag 參數來保存 setInterval 的回調,用來在鼠標停放在圖片上停止自動輪播,我們把這個 autoPlayFlag 作為組件的一個屬性來保存。

Slider.autoPlayFlag = null;

</div>

然后在組件初始化 componentDidMount 的時候調用這個函數:

componentDidMount() {
 this.goPlay();
}

</div>

  • 我們還需要一個 pausePlay 函數來暫停自動輪播。

    pausePlay() {
     clearInterval(this.autoPlayFlag);
    }
  • 最后就是 render 了,根據屬性配置哪些函數和組件需要~

  • 默認屬性,這個是需要的,在使用屬性的時候如果忘了一些配置項也不會出錯。
    Slider.defaultProps = {
      speed: 1,
      delay: 2,
      pause: true,
      autoplay: true,
      dots: true,
      arrows: true,
      items: [],
    };

樣式

什么,你告訴我顯示在頁面上的都是什么鬼?

來寫 scss 吧:

* {
  margin: 0;
  padding: 0;
}
.slider {
  overflow: hidden;
  width: 100%;
  position: relative;

  &>ul {
    height: auto;
    overflow: hidden;
    position: relative;
    left: 0;
    transition: left 1s;
  }

  .slider-item {
    display: inline-block;
    height: auto;

    &>img {
      display: block;
      height: auto;
      width: 100%;
    }
  }

  .slider-arrow {
    display: inline-block;
    color: #fff;
    font-size: 50px;
    position: absolute;
    top: 50%;
    margin-top: -50px;
    z-index: 100;
    padding: 20px;
    cursor: pointer;
    font-weight: bold;

    &:hover {
      background: rgba(0,0,0,.2);
    }

    &.slider-arrow-right {
      right: 0;
    }
    &.slider-arrow-left {
      left: 0;
    }
  }

  .slider-dots-wrap {
    z-index: 99;
    text-align: center;
    width: 100%;
    position: absolute;
    bottom: 0;

    .slider-dot {
      display: inline-block;
      width: 6px;
      height: 6px;
      border: 3px solid #ccc;
      margin: 6px;
      cursor: pointer;
      border-radius: 20px;

      &:hover {
        border: 3px solid #868686;
      }

      &.slider-dot-selected {
        background: #ccc;
      }
    }
  }
}

</div>

這里不多說了,樣式就這樣~有疑問可以 call me。

謝謝謝謝,謝謝各位客官光看 ~ :smile: :smile: 啊哈 :grin:

:smile: :smile: :grin: :dog: :poodle:

</div>

 本文由用戶 vjsc7136 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!