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

一个简单的轮播

为了更具有通用和参考性,轮播组件中,轮播只使用了react,没有添加其他的状态管理,或者参数类型限制的库. 所以这个轮播的方法,同样可以用于vue 等其他框架
github地址

最终效果

1433614-20181228164055910-856752302.gif

显示无限循环原理

如图所示,如果轮播里面有三个部分,那么可以在首端前添加一个跟最后一块一样的dom节点,同理在最末端添加跟首端相同的节点,这样当轮播到末端,在下一张的情况下,就可以无缝衔接首端的节点,然后当动画结束后,在直接切换到真正的首端,就实现了无缝衔接的轮播器
1433614-20181228110633753-1655602493.png
1433614-20181228163744494-1916927496.gif

组件代码
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>
            
        )
    }
}

转载于:https://www.cnblogs.com/slongs/p/10189439.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值