react 实现一个无限循环的轮播器 附github地址

Posted SSS小龙SSS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react 实现一个无限循环的轮播器 附github地址相关的知识,希望对你有一定的参考价值。

一个简单的轮播

为了更具有通用和参考性,轮播组件中,轮播只使用了react,没有添加其他的状态管理,或者参数类型限制的库. 所以这个轮播的方法,同样可以用于vue 等其他框架
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(() => {
                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({
            animationStep: this.props.animationStep,
            currentCarousel: currentIndex
        });
    }

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

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

    /**
     * @description 轮播的mouseout事件
     */
    handlerNext() {
        this.handlerCarousel(\'right\');
    }

    handlerPre() {
        this.handlerCarousel(\'left\');
    }

    getIndicatorsActive(index) {
        // 边界判断 使在轮播在边界的时候 导航下面的小标 也能正常的添加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地址的主要内容,如果未能解决你的问题,请参考以下文章

温故而知新 兼容性较强的轮播器superslide.js

自定义完美的ViewPager 真正无限循环的轮播图

react-native 一起来实现一个高性能循环滚动的轮播图吧!

基于UICollectionView的无限轮播器(封装)

JS框架_(Bootstrap框架)实现简单的轮播图

用原生的javascript 实现一个无限滚动的轮播图