React 剛出來不久,組件還比較少,不像 jquery 那樣已經有不少現成的插件了,當是就本身寫了一個基於 React 的輪播組件,固然只是一個小 demo,剛剛有用 es6 的語法從新改了改,就想着寫一個小教程給新手,如何實現一個 React 的小組件。
先放上倉庫地址,能夠先 clone 來看看代碼:https://github.com/TongchengQiu/react-slider。
react-slider 是一個圖片輪播的組件,支持的配置有 圖片(必須好很差,要否則輪播毛)、輪播圖片的速度、是否自動輪播、自動輪播的時候鼠標放上去是否暫停、自動輪播速度、是否須要先後箭頭、是否須要 dot (我不知道怎麼表述好,反正意思你懂)。css
首先,寫一個組件必須先考慮改組件的需求有哪些,支持的配置須要哪些。
如上已經說了改組件的需求:html
輪播的圖片react
配置輪播圖片切換的速度jquery
可配置是否自動輪播webpack
可配置自動輪播的時候鼠標放上去是否暫停git
可配置自動輪播的速度es6
可配置是否須要先後箭頭github
可配置是否須要 dot (我不知道怎麼表述好,反正意思你懂)web
這一步先到此爲止~~~數組
這裏咱們是使用 React 框架,固然也是用它的好搭檔 webpack 來構建自動化流程咯~?
不懂 webpack 的配置能夠看個人博客關於 webpack 使用的文章,謝謝~?
這是項目開發目錄的文件結構:
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的入口文件
看目錄結構咱們應該明白了,咱們主要關注 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') );
能夠看到,在使用 Slider 組件的時候,根據需求,咱們能夠傳入這些屬性來配置組件。
一個 items
數組,決定了須要輪播的內容,items 裏面每一個元素都是一個對象,有 src 和 alt 屬性,分別是輪播圖片的 src 地址和 alt 內容;speed
是圖片切換的時候的速度時間,須要配置一個 number 類型的數據,決定時間是幾秒;autoplay
是配置是否須要自動輪播,是一個布爾值;delay
是在須要自動輪播的時候,每張圖片停留的時間,一個 number 值;pause
是在須要自動輪播的時候,鼠標停留在圖片上,是否暫停輪播,是一個布爾值;dots
是配置是否須要輪播下面的小點;arrows
是配置是否須要輪播的先後箭頭;
首先咱們來考慮一下須要多少個組件,最外層的 Slider 組件是毋庸置疑的了。
根據需求咱們能夠分出一個 SliderItem 組件,一個 SliderDots,一個 SliderArrows 組件,分別是 輪播每一個圖片的item,輪播下面dots的組件,左右箭頭組件。
咱們先來編寫每一個小組件,組件是對外封閉獨立的,因此它只須要對外暴漏屬性的配置便可。
由於 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> ); } }
let width = 100 / count + '%';
便是計算它佔父組件的寬度百分比。
對於 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> ); } }
代碼能夠看出,咱們用 for 循環根據 count 來決定了須要多少個 dot,而後在每一個 dot 綁定函數傳入一個 i 值,而且若是這個 dot 對於當前顯示的輪播項,就多加一個class slider-dot-selected。
每一個 dot click 綁定的函數還須要計算須要向前或者向後移動多少個輪播項,而後回調 turn 函數。
對於 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> ); } }
這個組件下面有兩個箭頭,分別是向前和向後,回調的 turn 是 1 和 -1。
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;
在這裏咱們先是 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;
而後在組件初始化 componentDidMount 的時候調用這個函數:
componentDidMount() { this.goPlay(); }
咱們還須要一個 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; } } } }
這裏很少說了,樣式就這樣~
有疑問能夠 call me。
? ? ? ? ?
文章原文:使用 React 實現一個輪播組件
個人博客地址:qiutc.me歡迎來吐槽!!!