1. 程式人生 > >react 實現一個無限循環的輪播器 附github地址

react 實現一個無限循環的輪播器 附github地址

class 不為 cursor oot rect 動畫 參數類型 4.0 z-index

一個簡單的輪播

為了更具有通用和參考性,輪播組件中,輪播只使用了react,沒有添加其他的狀態管理,或者參數類型限制的庫.
github地址

最終效果

技術分享圖片

顯示無限循環原理

如圖所示,如果輪播裏面有三個部分,那麽可以在首端前添加一個跟最後一塊一樣的dom節點,同理在最末端添加跟首端相同的節點,這樣當輪播到末端,在下一張的情況下,就可以無縫銜接首端的節點,然後當動畫結束後,在直接切換到真正的首端,就實現了無縫銜接的輪播器
技術分享圖片

組件代碼
import React, {Component} from ‘react‘;
import style from ‘./style.use.less‘;

export default class Test extends Component {
    static defaultProps = {
        step: 2000,
        animationStep: 1
    }

    constructor(props) {
        super(props)
        this.state = {
            currentCarousel: 0,
            translateList: [],
            animationStep: 0,
        }

        this.handleCarouselBodyMouseOver = this.handleCarouselBodyMouseOver.bind(this);
        this.handleCarouselBodyMouseOut = this.handleCarouselBodyMouseOut.bind(this);
        this.handleCarouselFooterMouseOver = this.handleCarouselFooterMouseOver.bind(this);
        this.renderIndicators = this.renderIndicators.bind(this);
        this.getIndicatorsActive = this.getIndicatorsActive.bind(this);
        this.handlerNext = this.handlerNext.bind(this);
        this.handlerTransitionEnd = this.handlerTransitionEnd.bind(this);
        this.handlerPre = this.handlerPre.bind(this);
    }

    componentWillMount() {
        style.use()
    }

    componentWillUnmount() {
        style.unuse()
        this.stopCarousel()
    }

    componentDidMount() {
        this.setState({
            animationStep: this.props.animationStep
        })
        this.startCarousel()
        
    }

    /**
     * @description 開始輪播
     */
    startCarousel() {
        this.stopCarousel()
        this.timerID = setInterval(() => {
                console.log(this.state.currentCarousel)
                this.handlerCarousel()  
            },
            this.props.step
        );
    }

    /**
     * @description 更改當前循環到的輪播下標
     * @param {String} type 運動的方向類型
     */
    handlerCarousel(type) {
        let direction = 1;
        if (type === ‘left‘) {  // 向做運動 下標減1
            direction = -1;
        }

        if (this.state.currentCarousel % (this.props.children.length + 1) !== this.props.children.length && this.state.currentCarousel >= 0) { // 下標不為-1 或者最後一項的情況下正常 遞增或者遞減
            this.setState(pre => {
                pre.currentCarousel += direction;
                return {
                    animationStep: this.props.animationStep, // 運動動畫 設置值  因為 當坐標在邊界的時候 會取消動畫時間 所以在不為邊界的時候 要恢復動畫時間 不然切換無輪播動畫
                    currentCarousel: pre.currentCarousel % (this.props.children.length + 1)
                }
            })
        }
        
    }

    /**
     * @description 監聽動畫結束 在下標為邊界時 並且切換的動畫結束 取消動畫 並調整輪播位置
     */
    handlerTransitionEnd() {
        // 當在最末端的時候 取消動畫 並將坐標重制為0
        if (this.state.currentCarousel % (this.props.children.length + 1) === this.props.children.length) {
            this.setState(pre => {
                return {
                    animationStep: 0,
                    currentCarousel: 0,
                }
            })
        } 
        // 當在最前端的時候 取消動畫 並將坐標重制為最大
        else if (this.state.currentCarousel < 0) {
            this.setState(pre => {
                return {
                    animationStep: 0,
                    currentCarousel: this.props.children.length - 1,
                }
            })
        }
    }

    /**
     * @description  停止輪播
     */
    stopCarousel() {
        clearInterval(this.timerID);
    }


    /**
     * @description 指示按鈕的mouseover事件
     */
    handleCarouselFooterMouseOver(currentIndex) {
        this.setState({
            currentCarousel: currentIndex
        });
    }

    /**
     * @description 輪播的mouseover事件
     */
    handleCarouselBodyMouseOver() {
        this.stopCarousel();
    }

    /**
     * @description 輪播的mouseout事件
     */
    handleCarouselBodyMouseOut() {
        this.startCarousel();
    }

    /**
     * @description 輪播的mouseout事件
     */
    handlerNext() {
        this.handlerCarousel(‘right‘);
    }

    handlerPre() {
        this.handlerCarousel(‘left‘);
    }

    getIndicatorsActive(index) {
        let active;
        // 邊界判斷 使在輪播在邊界的時候 導航下面的小標 也能正常的添加active狀態
        if (this.state.currentCarousel === index || this.state.currentCarousel === index + this.props.children.length || this.state.currentCarousel < 0 && index === this.props.children.length - 1) {
            return ‘active‘;
        }
        return ‘‘
    }

    /**
     * @description 導航的指示按鈕
     */
    renderIndicators() {
        
        return (
            <div className = ‘carousel-footer‘>
                <ul className = ‘indicators-container‘>
                    {this.props.children.map((item, index) => {
                        let active = this.getIndicatorsActive(index)
                        return <li onMouseOver = {() => this.handleCarouselFooterMouseOver(index)} className = {`indicators-item ${active}`} key = {index} ></li>
                    })}
                </ul>
            </div>
        )
    }

    render() {
        return( 
            <div className = ‘carousel-container‘ onMouseOver = {this.handleCarouselBodyMouseOver} onMouseOut = {this.handleCarouselBodyMouseOut}>
                <div className = ‘carousel-body‘ onTransitionEnd = {this.handlerTransitionEnd} style = {{transition: `transform ${this.state.animationStep}s`,width: `${(this.props.children.length+2)*100}%`, transform: `translateX(${-100/(this.props.children.length+2)*(this.state.currentCarousel+1)}%)`}}>
                    <div className = {`carousel-item`} style = {{width: `${100/(this.props.children.length+2)}%`}} key = {‘strat‘} >{this.props.children[this.props.children.length-1]}</div>
                    {this.props.children.map((item, index) => {
                        return <div className = {`carousel-item`} style = {{width: `${100/(this.props.children.length+2)}%`}} key = {index} >{item}</div>
                    })}
                     <div className = {`carousel-item`} style = {{width: `${100/(this.props.children.length+2)}%`}} key = {‘end‘} >{this.props.children[0]}</div>
                </div>
                {this.renderIndicators()}
                <div className = ‘btn-container‘>
                    <div className = ‘btn-direction pre‘ onClick = {this.handlerPre}>{‘<‘}</div>
                    <div className = ‘btn-direction next‘ onClick = {this.handlerNext}>{‘>‘}</div>  
                </div>
            </div>
        )
    }
}
組件csss
.carousel-container {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    .carousel-body {
        height: 100%;
        .carousel-item {
            display: inline-block;
            width: 100%;
            height: 100%;
        }
    }
    .carousel-footer {
        position: absolute;
        width: 100%;
        bottom: 0;

        .indicators-container {
            text-align: center;
            padding: 0;
            .indicators-item {
                list-style: none;
                display: inline-block;
                opacity: .48;
                width: 30px;
                height: 2px;
                background-color: #fff;
                border: none;
                outline: none;
                padding: 0;
                margin: 0;
                cursor: pointer;
                transition: .3s;
                margin: 0 4px;

                &.active {
                    opacity: 1;
                }
            }
        }
    }

    .btn-container {
        position: absolute;
        width: 100%;
        top: 50%;
        transform: translateY(-50%);
        
        .btn-direction {
            padding: 0;
            margin: 0;
            height: 42px;
            line-height: 38px;
            width: 36px;
            cursor: pointer;
            transition: .3s;
            border-radius: 50%;
            background-color: rgba(31,45,61,.11);
            color: #fff;
            top: 50%;
            z-index: 10;
            text-align: center;
            font-size: 12px;
            display: inline-block;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            opacity: 0;
           
        }

        .pre {
            left: 12px;
        }

        .next {
            right: 12px;
        }

    }

   
    
}
.carousel-container:hover .btn-direction{
    opacity: 1;
    
    &.pre {
        left: 24px;
    }

    &.next {
        right: 24px;
    }
}
最終調用
import React, {Component} from ‘react‘;
import style from ‘./style.use.less‘
import Carouse from ‘../../components/Carousel/Carousel‘

export default class Test extends Component {
    componentWillMount() {
        style.use()
    }

    componentWillUnmount() {
        style.unuse()
    }

    render() {
        return ( 
            <div className = ‘carouse‘>   
                <Carouse>
                    <div className = ‘box‘>1</div>
                    <div className = ‘box‘>2</div>
                    <div className = ‘box‘>3</div>
                    <div className = ‘box‘>4</div>
                </Carouse>
            </div>
            
        )
    }
}

react 實現一個無限循環的輪播器 附github地址