• React封装公共组件:轮播图(2)


    上一篇文章中,我们介绍了如何实现轮播图的无缝滚动

    这一篇文章将会介绍如何实现自动播放,以及如何将自动播放和手指滑动这两个事件进行隔离

    自动播放

    假设自动播放的顺序为:图片无限向左滚动

    那么当我们发现 this.current 指向第二组最后一张图片时,也应该让它瞬间移动到第一组的最后一张图片

    直接看代码:

    autoPlaySlider = () => {
        
        clearInterval(this.intervalID)
        this.intervalID = setInterval(() => {
            if(this.current === this.dotLength*2 -1){
                this.current = this.dotLength - 1
                this.translatex = this.current * -this.imgWidth
                this.list.current.style.transition = 'none'
                this.list.current.style.transform = `translateX(${this.translatex}px)`
    
                clearTimeout(this.timerID)
                this.timerID = setTimeout(() => {
                    this.current++
                    this.translatex = this.current * -this.imgWidth
                    this.list.current.style.transition = '0.4s'
                    this.list.current.style.transform = `translateX(${this.translatex}px)`
                    this.formatDots()
                }, 0);
    
            }else{
                this.current++
                this.translatex = this.current * -this.imgWidth
                this.list.current.style.transition = '0.4s'
                this.list.current.style.transform = `translateX(${this.translatex}px)`
                this.formatDots()
            }
    
        }, 1500);
        
      }
    

    当 this.current 指向第二组最后一张图片时,必须要做2次transform的动作

    第一次:从“第二组最后一张” -> “第一组最后一张”

    第二次:从“第一组最后一张” -> “第二组第一张”

    为了将这两个动作完全隔离,我们用了一个计时器timerID,将 第二次的滚动动作 延迟执行

    如果不这样做,第二次的动作将会覆盖第一次的动作,导致动画效果出错,变成“向右滚动”

    兼容自动播放和手指触摸

    我们的第一个目标是:当手指开始滑动图片时,停止自动播放

    当 touchStart 被触发时,清空页面上所有与自动播放相关的计时器

    然后给这个“进程”上一个锁,将this.touching设置为true

    并在touchend 结束时,将 this.touching 改为 false

    我们的第二个目标是:当手指停止滑动图片后,过一段时间,重新开始自动播放

    当touchend结束时,开启一个名为 waitForAutoPlay 的计时器,3s之后,执行autoPlaySlider函数

    并在autoPlaySlider函数开头,根据 this.touching的值 多加一层判断

    autoPlaySlider = () => {
        if(this.touching){
          return
        }else{
          ...
        }
    }
    

    最后,别忘了在组件卸载之前,清空页面上的所有计时器

    componentWillUnmount() {
        clearInterval(this.intervalID)
        clearTimeout(this.timerID)
        clearTimeout(this.waitForAutoPlay)
    }
    

    完整代码

    Slider.js的完整代码如下

    import React, {Component, Fragment} from 'react'
    import {
      SliderWrapper,
      SliderList,
      SliderDot
    } from './style'
    
    class Slider extends Component {
      constructor(props) {
        super(props)
        this.wrap = React.createRef()
        this.list = React.createRef()
        this.dot = React.createRef()
        this.dotLength = 0
        this.startPoint = {}
        this.distance = {}
        this.current = 0
        this.translatex = 0
        this.startOffset = 0
        this.imgWidth = this.props.imgWidth
        this.threshold = 0.2 //滑动切换阈值
        this.touching = false
        this.isMove = false
        this.intervalID = null
        this.timerID = null
      }
    
    
      render() {
        return (
          <Fragment>
          <SliderWrapper
            ref={this.wrap}
            width={this.props.imgWidth}
            onTouchStart={this.handleTouchStart}
            onTouchMove={this.handleTouchMove}
            onTouchEnd={this.handleTouchEnd}
          >
            <SliderList ref={this.list} >
              {
                this.props.imgList.map((item,index) => {
                  return (
                    <li key={index}><img src={item} width={this.props.imgWidth}/></li>
                  )
                })
              }
            </SliderList>
            <SliderDot ref={this.dot}>
              {
                this.props.imgList.map((item,index) => {
                  return (
                    <li key={index}
                    ></li>
                  )
                })
              }
            </SliderDot>
            
          </SliderWrapper>
          </Fragment>
        )
      }
    
      componentDidMount() {
        this.initSlider()
      }
    
      initSlider = () => {
        this.dotLength = this.dot.current.childNodes.length
        this.list.current.innerHTML+=this.list.current.innerHTML
        this.dot.current.childNodes[0].classList.add('active')
    
        this.autoPlaySlider()
      }
    
    
      autoPlaySlider = () => {
        if(this.touching){
          return
        }else{
          clearInterval(this.intervalID)
          this.intervalID = setInterval(() => {
        
            if(this.current === this.dotLength*2 -1){
              this.current = this.dotLength - 1
              this.translatex = this.current * -this.imgWidth
              this.list.current.style.transition = 'none'
              this.list.current.style.transform = `translateX(${this.translatex}px)`
              
      
              clearTimeout(this.timerID)
              this.timerID = setTimeout(() => {
                this.current++
                this.translatex = this.current * -this.imgWidth
                this.list.current.style.transition = '0.4s'
                this.list.current.style.transform = `translateX(${this.translatex}px)`
                this.formatDots()
              }, 0);
      
            }else{
              this.current++
              this.translatex = this.current * -this.imgWidth
              this.list.current.style.transition = '0.4s'
              this.list.current.style.transform = `translateX(${this.translatex}px)`
              this.formatDots()
            }
      
          }, 1500);
        }
      }
    
      handleTouchStart = (ev) => {
        clearTimeout(this.timerID)
        clearInterval(this.intervalID)
        clearTimeout(this.waitForAutoPlay)
        this.touching = true
        let touch = ev.changedTouches[0]
        this.startPoint = {
          x: touch.pageX,
          y: touch.pageY
        }
    
        if(this.current === 0){
          this.current = this.dotLength
        }else if(this.current === this.dotLength*2 -1){
          this.current = this.dotLength -1
        }
    
        this.translatex = this.current * -this.imgWidth
        this.startOffset = this.translatex
        this.list.current.style.transition = 'none'
        this.list.current.style.transform = `translateX(${this.translatex}px)`
      }
    
      handleTouchMove = (ev) => {
        let touch = ev.changedTouches[0]
        this.distance = {
          x: touch.pageX - this.startPoint.x,
          y: touch.pageY - this.startPoint.y
        }
        
        this.translatex = this.startOffset + this.distance.x
        this.list.current.style.transform = `translateX(${this.translatex}px)`
      }
    
      handleTouchEnd = (ev) => {
        if(Math.abs(this.distance.x) > this.imgWidth * this.threshold){
    
          if(this.distance.x>0){
            this.current--
          }else{
              this.current++
          }
        }
    
        this.translatex = this.current * -this.imgWidth
        this.list.current.style.transition = '0.3s'
        this.list.current.style.transform = `translateX(${this.translatex}px)`
        this.formatDots()
        this.touching = false
        
        this.waitForAutoPlay = setTimeout(() => {
          if(!this.touching){
            this.autoPlaySlider()
          }
        }, 3000);
      }
    
      formatDots() {
        Array.from(this.dot.current.childNodes).forEach((item,index) => {
          item.classList.remove('active')
          if(index === (this.current%this.dotLength)){
            item.classList.add('active')
          }
        })
      }
    
      componentWillUnmount() {
        clearInterval(this.intervalID)
        clearTimeout(this.timerID)
        clearTimeout(this.waitForAutoPlay)
      }
    }
    
    export default Slider
    
  • 相关阅读:
    关于pipe管道的读写端关闭问题
    线性表的链式存储——C语言实现
    关于无法解析的外部符号 _main
    Tomcat域名与服务器多对多配置
    JavaScript基础
    Vue.js入门
    SpringBoot注解大全,收藏一波!!!
    数据库连接错误
    SpringBoot入门
    MyBatis插入并返回id技巧
  • 原文地址:https://www.cnblogs.com/baebae996/p/14386663.html
Copyright © 2020-2023  润新知