React 轮播图项目

Banner.js

import React from 'react';
import PropTypes from 'prop-types';
import '../static/css/banner.css';

export default class Banner extends React.Component {
    //=>设置属性的默认值和规则
    static defaultProps = {
        data: [],
        interval: 3000,
        step: 1,
        speed: 300
    };
    static propTypes = {
        data: PropTypes.array,
        interval: PropTypes.number,
        step: PropTypes.number,
        speed: PropTypes.number
    };

    constructor(props) {
        super(props);

        //=>INIT STATE
        let {step, speed} = this.props;
        this.state = {
            step,
            speed
        };
    }

    //=>数据的克隆
    componentWillMount() {
        let {data} = this.props,
            cloneData = data.slice(0);
        cloneData.push(data[0]);
        cloneData.unshift(data[data.length - 1]);
        this.cloneData = cloneData;//=>挂载到实例上供其它方法调用
    }

    //=>自动轮播
    componentDidMount() {
        //=>把定时器返回值挂载到实例上,方便后期清除:结束自动轮播
        this.autoTimer = setInterval(this.autoMove, this.props.interval);
    }

    componentWillUpdate(nextProps, nextState) {
        //=>边界判断:如果最新修改的STEP索引大于最大索引(说明此时已经是末尾了,不能在向后走
        //了),我们让其立即回到(无动画)索引为1的位置
        if (nextState.step > (this.cloneData.length - 1)) {
            this.setState({
                step: 1,
                speed: 0
            });
        }
    }

    componentDidUpdate() {
        //=>只有是从克隆的第一张立即切换到真实第一张后,我们才做如下处理:让其从当前第一张运动
        //到第二张即可
        let {step, speed} = this.state;
        if (step === 1 && speed === 0) {
            //=>为啥要设置定时器延迟:CSS3的TRASITION有一个问题(主栈执行的时候,短时间内
            //遇到两次设置TRANSITION-DRRATION的代码,以最后一次设置的为主)
            let delayTimer = setTimeout(() => {
                clearTimeout(delayTimer);
                this.setState({
                    step: step + 1,
                    speed: this.props.speed
                });
            }, 0);
        }
    }

    render() {
        let {data} = this.props,
            {cloneData} = this;
        if (data.length === 0) return '';

        //=>控制WRAPPER的样式
        let {step, speed} = this.state,
            wrapperSty = {
                width: cloneData.length * 1000 + 'px',
                left: -step * 1000 + 'px',
                transition: `left ${speed}ms linear 0ms`
            };

        return <section className='container'>
            <ul className='wrapper' style={wrapperSty}>
                {cloneData.map((item, index) => {
                    let {title, pic} = item;
                    return <li key={index}>
                        <img src={pic} alt={title}/>
                    </li>;
                })}
            </ul>
            <ul className='focus'>
                {data.map((item, index) => {
                    let tempIndex = step - 1;
                    step === 0 ? tempIndex = data.length - 1 : null;
                    step === (cloneData.length - 1) ? tempIndex = 0 : null;
                    return <li className={tempIndex === index ? 'active' : ''}
                               key={index}></li>;
                })}
            </ul>
            <a href="javascript:;" className="arrow arrowLeft"></a>
            <a href="javascript:;" className="arrow arrowRight"></a>
        </section>;
    }

    //=>向右切换
    autoMove = () => {
        this.setState({
            step: this.state.step + 1
        });
    };
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Banner from "./component/Banner";

//=>公共的样式资源在INDEX中导入(组件独有的样式可以在组件内部导入)
import './static/css/reset.min.css';

//=>在REACT的JSX中需要使用图片等资源的时候,资源的地址不能使用相对地址(因为经过WEBPACK编译后,资源地址的路径已经改变了,原有的相对地址无法找到对应的资源),此时我们需要基于ES6 Module或者CommonJS等模块导入规范,把资源当做模块导入进来(或者我们使用的图片地址都是网络地址)
let IMG_DATA = [];
for (let i = 1; i <= 5; i++) {
    IMG_DATA.push({
        id: i,
        title: '',
        pic: require(`./static/images/${i}.jpg`)
    });
}

//=>调取组件渲染
ReactDOM.render(<main>
    {/*
      * DATA:轮播图需要绑定的数据 (空数组)
      * INTERVAL:自动轮播间隔的时间 (3000MS)
      * STEP:默认展示图片的索引(记住前后各克隆了一张) (1)
      * SPEED:每一张切换所需要的运动时间 (300MS)
      */}
    <Banner data={IMG_DATA}
            interval={1000}
            step={1}
            speed={300}/>

    <div style={{margin: '20px auto'}}></div>

    <Banner data={IMG_DATA.slice(2)}
            interval={5000}
            step={3}/>
</main>, root);

Banner.css

.container {
    position: relative;
    margin: 0 auto;
    width: 1000px;
    height: 300px;
    overflow: hidden;
    text-align: center;
}

.container:hover .arrow {
    display: block;
}

.container .wrapper {
    position: absolute;
    top: 0;
    left: 0;
    width: 6000px;
    height: 100%;
    overflow: hidden;
}

.container .wrapper li {
    float: left;
    width: 1000px;
    height: 100%;
}

.container .wrapper li img {
    display: block;
    width: 100%;
    height: 100%;
}

.container .focus {
    display: inline-block;
    position: relative;
    z-index: 999;
    top: 270px;

    padding: 4px;
    font-size: 0;
    background: rgba(0, 0, 0, 0.3);
    border-radius: 20px;
}

.container .focus li {
    display: inline-block;
    margin: 0 4px;
    width: 12px;
    height: 12px;
    background: rgba(255, 255, 255, .8);
    border-radius: 50%;
    cursor: pointer;
}

.container .focus li.active {
    background: lightcoral;
}

.container .arrow {
    display: none;
    position: absolute;
    top: 50%;
    margin-top: -22.5px;
    width: 28px;
    height: 45px;
    background: url("../images/pre.png") no-repeat;
    opacity: 0.5;
}

.container .arrow:hover {
    opacity: 1;
}

.container .arrow.arrowLeft {
    left: 0;
}

.container .arrow.arrowRight {
    right: 0;
    background-position: -50px 0;
}

实现轮播图左右切换

Banner.js

import React from 'react';
import '../static/css/banner.css';

export default class Banner extends React.Component {
    static defaultProps = {
        data: [],
        interval: 3000,
        step: 1,
        speed: 300
    };

    constructor(props) {
        super(props);

        //=>初始化状态
        let {step, speed} = this.props;
        this.state = {
            step,
            speed
        };
    }

    componentWillMount() {
        /*数据克隆*/
        let {data} = this.props,
            cloneData = data.slice(0);
        cloneData.push(data[0]);
        cloneData.unshift(data[data.length - 1]);
        this.cloneData = cloneData;
    }

    componentDidMount() {
        /*开启自动轮播*/
        this.autoTimer = setInterval(this.moveRight, this.props.interval);
    }

    componentWillUpdate(nextProps, nextState) {
        /*向右边界判断:如果当前最新修改的STEP值已经大于最大的索引,说明不能继续向右走了,我们应该让其“立即”(无动画)回到真实第一张(STEP=1)*/
        if (nextState.step > (this.cloneData.length - 1)) {
            this.setState({
                step: 1,
                speed: 0
            });
        }
        /*向左边界判断:如果当前最新修改的索引已经小于零,说明不能继续向左走了,我们让其立即回到“倒数第二张”图片位置(真实的最后一张图片) STEP=CLONE-DATA.LENGTH-2*/
        if (nextState.step < 0) {
            this.setState({
                step: this.cloneData.length - 2,
                speed: 0
            });
        }
    }

    componentDidUpdate() {
        /*向右边界判断:立即回到第一张后,我们应该让其运动到真实的第二张*/
        let {step, speed} = this.state;
        if (step === 1 && speed === 0) {
            let delayTimer = setTimeout(() => {
                clearTimeout(delayTimer);
                this.setState({
                    step: step + 1,
                    speed: this.props.speed
                });
            }, 0);
        }

        /*向左边界判断:立即回到“倒数第二张”后,我们应该让其向回在运动一张*/
        if (step === this.cloneData.length - 2 && speed === 0) {
            let delayTimer = setTimeout(() => {
                clearTimeout(delayTimer);
                this.setState({
                    step: step - 1,
                    speed: this.props.speed
                });
            }, 0);
        }
    }

    render() {
        let {data, style} = this.props,
            {cloneData} = this;
        if (data.length === 0) return '';

        //=>计算WRAPPER样式
        let {step, speed} = this.state,
            wrapperStyle = {
                width: `${cloneData.length * 1000}px`,
                transform: `translateX(${-step * 1000}px)`,
                transition: `transform ${speed}ms`
            };

        return <section className={'container'}
                        onMouseEnter={this.movePause}
                        onMouseLeave={this.movePlay}
                        onClick={this.handleClick}>
            <ul className={'wrapper'} style={wrapperStyle}
                onTransitionEnd={() => {
                    //=>当WRAPPER切换动画完成(切换完成),再去执行下一次切换任务
                    this.isRun = false;
                }}>
                {cloneData.map((item, index) => {
                    let {pic, title} = item;
                    return <li key={index}>
                        <img src={pic} alt={title}/>
                    </li>;
                })}
            </ul>
            <ul className={'focus'}>
                {data.map((item, index) => {
                    /*焦点对齐:图片索引减去一就是焦点选中项对应的索引(特殊的:如果图片索引是零,让最后一个焦点选中,如果图片索引是最大,让第一个焦点选中)*/
                    let tempIndex = step - 1;
                    step === 0 ? tempIndex = data.length - 1 : null;
                    step === cloneData.length - 1 ? tempIndex = 0 : null;

                    return <li key={index} className={index === tempIndex ? 'active' : ''}></li>;
                })}
            </ul>
            <a href="javascript:;" className="arrow arrowLeft"></a>
            <a href="javascript:;" className="arrow arrowRight"></a>
        </section>;
    }

    //=>向右切换:自动轮播或者点击有切换按钮
    moveRight = () => {
        this.setState({
            step: this.state.step + 1
        });
    };

    //=>自动轮播的暂停和开启
    movePause = () => clearInterval(this.autoTimer);
    movePlay = () => this.autoTimer = setInterval(this.moveRight, this.props.interval);

    //=>事件委托
    handleClick = ev => {
        let target = ev.target,
            tarTag = target.tagName,
            tarClass = target.className;
        //=>左右切换按钮
        if (tarTag === 'A' && /(^| +)arrow( +|$)/.test(tarClass)) {
            //=>防止过快点击
            if (this.isRun) return;
            this.isRun = true;
            //=>RIGHT
            if (tarClass.indexOf('arrowRight') >= 0) {
                this.moveRight();
                return;
            }
            //=>LEFT
            this.setState({
                step: this.state.step - 1
            });
            return;
        }
    };
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import ReactSwipe from 'react-swipe';

/*导入公共样式和资源*/
import './static/css/reset.min.css';
import './static/css/index.css';
import Banner from "./component/Banner";

/*准备数据:在JSX中需要加载一些资源(例如IMG图片),此时我们不能使用相对地址,WEBPACK打包后资源地址都是要改变的,可以使用网络绝对地址或者基于模块规范把图片导入进来使用*/
let IMG_DATA = [];
for (let i = 1; i <= 3; i++) {
    IMG_DATA.push({
        id: i,
        title: '',
        pic: require(`./static/images/${i}.jpg`)
    });
}

/*RENDER渲染*/

ReactDOM.render(<main>
    {/!*BANNER*!/}
    <Banner data={IMG_DATA}
            interval={1000}
            step={1}
            speed={300}/>

    <Banner data={IMG_DATA}
            interval={3000}/>
</main>, root);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值