美文网首页
使用react封装图片轮播效果

使用react封装图片轮播效果

作者: darayo | 来源:发表于2018-12-26 23:41 被阅读0次

    实现图片轮播的基本原理

    • 有一个视窗容器显示当前图片内容,容器之外的内容都被隐藏
    • 在视窗容器中,有一个包含一组图片的子容器改变其水平或者垂直方向的,形成轮播
    轮播图位置变化示意图

    上图的蓝色框表示视窗容器,蓝色框之外的区域是被隐藏的。当橙色区域的left或者tranlateX为-200时,可视范围由1变成2。此时轮播图为向右翻页。

    <div class="container"> <!--视窗容器-->
        <div class="slider-content" style="left: -200px;"><!--图片容器-->
            <div class="slider-item">1</div>
            <div class="slider-item">2</div>
            <div class="slider-item">3</div>
        </div>
    </div>
    

    接下来得思考轮播图应该具有的哪些功能点:

    • 自动播放
    • 无缝循环播放
    • 点击按钮翻页
    • 拖动翻页
    轮播图的自动播放

    自动播放可以理解为定时执行,就是间隔几秒去修改容器的位置。因此使用setInterval或者setTimeout即可。

    以下代码简单是实现了自动轮播的效果。

    var content = document.querySelector('.slider-content');
    
    class Slider  {
       constructor(ele){
        this.ele = ele;
        this.width = this.ele.parentNode.offsetWidth; // 获取宽度
        this.length = document.querySelectorAll('.slider-item').length; 
        this.animate(); 
       }
    
       move() {
        let ele = this.ele;
        if(parseInt(ele.style.left) > -(this.length*this.width)) {
            // 设置容器改变的left值
            ele.style.left = parseInt(ele.style.left) - this.width +'px';
        }else{
            ele.style.left = 0;
        }   
      }
    
      animate() {
        setInterval(this.move.bind(this),2000)
      }
    }
    
    var box = new Slider(content);
    

    观察上面的效果可以发现,当播放到最后一个图片,再回到第一页时,出现了空白,显然这个效果不理想,以上代码的边界逻辑存在问题,没有实现无缝循环播放。

    无缝循环播放

    实现无缝循环播放,我们需要向slider-content中的末尾添加上第1页。如下图

    无缝循环播放示意图

    html结构

    <div class="container"> <!--视窗容器-->
        <div class="slider-content" style="left: -200px;"><!--图片容器-->
            <div class="slider-item">1</div>
            <div class="slider-item">2</div>
            <divclass="slider-item">3</div>
            <div class="slider-item">1</div>
        </div>
    </div>
    
    var content = document.querySelector('.slider-content');
    
    class Slider  {
        constructor(ele){
            this.ele = ele;
            this.width = this.ele.parentNode.offsetWidth; // 获取宽度
            this.length = document.querySelectorAll('.slider-item').length;
            this.index = 0; // 当前页的索引值
            this.animate();        
        }
    
        move () {
            // 当前页的索引值等于最后一页,重置索引值为第一页
            if (this.index === this.length - 1) {
                this.index = 0;
            }
              
            this.index ++;
            this.ele.style.left = - this.index * this.width + 'px';     
        }
    
    
        animate() {
            setInterval(this.move.bind(this),2000)
        }
    }
    
    var box = new Slider(content);
    

    以上代码新增了索引值index,用于记录当前窗口显示的是第几张图,当索引值不断递增,直到等于length-1时,表示显示的是最后一张。length-1就是边界点,此时将索引值重置为0,轮播图就实现了循环。

    相反的往左翻页index--,递减的边界值为0,当index为0时,将index的值设置为length-1。

    点击按钮翻页

    点击按钮翻页,实际就是操作index值,向左翻页则index--,向右翻页则index++。需要注意的是,在操作翻页时,最好将定时器清除,以免造成效果混乱。

    结合拖拽在react中封装轮播图

    class Slider extends React.Component {
      constructor(props){
        super(props); 
        // props为组件传递的配置项
        this.speed = this.props.speed || 2000;  // 播放速度
        this.direction = this.props.direction || 'X'; // 翻页方向
        this.auto = this.props.auto || true; // 是否自动播放
        this.effect = this.props.effect ||'easeIn'; // 动效方式
        this.data = this.props.data || []; // 页面数据内容
        this.length = this.data.length + 1; // 页面数量
        this.width = this.props.width || 320; // 容器宽度
        this.height = this.props.height || 200; // 容器高度
        this.index = 0; // 当前页索引值
        this.timer = 0; // 定时器ID
        /**
         * 是否正在自动滚动
         */
        this.autoRun = false;
    
        // 根据配置的方向,设置翻页的值
        if(this.direction === 'X') { 
          this.dir = parseInt(this.width);
        }else {
          this.dir = parseInt(this.height);
        }
    
        if(this.auto) {
          this.autoRun = true;
          this.timer = setInterval(this.autoPlay, this.speed);    
        }
      }
    
      /**
       * 移动到第几页
       * @param {number} index 第几页
       * @param {string} direction 方向,从 'left, right, up, down' 中取一个作为值
       */
      setIndex = (direction) => {
        let to = 0;
        const ele = this.refs.sliderContent.refs.dragElement;
    
        /**
         * 点击左侧按钮,index --,边界点为0
         */
        if (direction === 'left') {
          // 初始位置就是极限位置的时候
          if (this.index === 0) {
            this.index = this.length - 1;
            ele.style.transform = `translate${this.direction}(${- this.index * this.dir}px)`;
          }
          to = - (this.index - 1) * this.dir;
          this.index --;
        }
    
        /**
         * 点击右侧按钮,到下一页,index ++,边界点为length-1
         */
        if (direction === 'right') {
          if (this.index === this.length - 1) {
            this.index = 0;
            ele.style.transform = `translate${this.direction}(${0}px)`;
          }
          to = - (this.index + 1) * this.dir;
          this.index ++;
        }
    
        // 此处结合了封装的运动框架
        animate(ele, this.effect, { 
          to,
          direction: this.direction
        }, () => {
          if (direction === 'right' && this.index === this.length - 1) {
            ele.style.transform = `translate${this.direction}(${0}px)`;
            this.index = 0;
          }
          if (direction === 'left' && this.index === 0) {
            ele.style.transform = `translate${this.direction}(${- this.index * this.dir}px)`;
            this.index = this.length - 1;
          }
    
          if (!this.autoRun) {
            this.timer = setInterval(() => this.setIndex(direction), this.speed);
            this.autoRun = true;
          }
        })
      }
    
      clickPrev = () => {
        this.autoRun = false;
        clearInterval(this.timer);
        this.setIndex('left');
      }
    
      clickNext = () => {
        this.autoRun = false;
        clearInterval(this.timer);
        this.setIndex('right');
      }
    
      autoPlay = () => {
        this.setIndex('right');
      }
    
      // 此处结合了封装的拖拽组件,获取滑动的差值,判断滑动方向
      touchEnd = (dis) => {
        let that = this;
        this.autoRun = false;
        clearInterval(this.timer);
    
        if(Math.abs(dis[this.direction]) > 60) {     
          dis[this.direction] > 0 ? this.clickNext() : this.clickPrev();      
        }
    
      }
    
      render() {
        const sliderItem = [...this.data, this.data[0]];
        return (
        <div className="container" ref="container" >
            <div className="prev" onClick={this.clickPrev}></div>
            <div className="next" onClick={this.clickNext}></div>
            <div ref="sliderContent" >
              <Drag className="sliderContent" onDragEnd={this.touchEnd}>           
                {sliderItem.map((item,i) =>
                  <li className="sliderItem" key={i}>{item}</li>)
                }
              </Drag>
            </div>
            <Drag className="sliderContent" ref="sliderContent" onDragEnd={this.touchEnd}>
              {sliderItem.map((item,i) =>
                <li className="sliderItem" key={i}>{item}</li>)
              }
            </Drag>
          </div>
        )
      }
    }
    
    export default Slider;
    

    以上轮播的封装,结合了上一篇的拖拽封装和另一个运动封装,完整的代码可以点击这里查看

    相关文章

      网友评论

          本文标题:使用react封装图片轮播效果

          本文链接:https://www.haomeiwen.com/subject/nzfklqtx.html