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


    需求分析

    1. 移动端触摸滑动:图片可以跟随手指滑动而滑动
    2. 底部小圆点:与轮播图联动的显示效果
    3. 无缝循环滚动:第一张图可以往前滑动、最后一张图也可以往后滑动
    4. 可以自动播放(下一篇文章介绍)

    页面样式和布局

    <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>
    

    其中,SliderWrapper是外层容器,它必须有固定的宽度,并设置overflow: hidden

    而SliderList内部则需要从左到右、水平排列

    图片滚动的核心操作为:改变SliderList的 translateX 值

    style-component文件:

    import styled from 'styled-components'
    
    export const SliderWrapper = styled.div`
       ${props => props.width + 'px'};
      overflow: hidden;
      margin: 100px auto;
      box-shadow: 0 0 5px 0 rgba(0,0,0,0.1);
      position: relative;
      background: #000;
      ul{
        margin: 0;
        padding: 0;
        list-style: none
      }
    `
    
    export const SliderList = styled.ul`
      display: flex;
      float: left;
      img {
         ${props => props.width + 'px'};
        vertical-align: top;
      }
    `
    
    export const SliderDot = styled.ul`
       100%;
      display: flex;
      justify-content: center;
      position: absolute;
      bottom: 8px;
      li {
         8px;
        height: 8px;
        border-radius: 4px;
        background: #fff;
        margin: 0 5px;
        transition: 0.2s;
        box-shadow: 0 0 3px 0 rgba(0,0,0,0.3);
        &.active {
           16px;
        }
      }
    `
    

    获取DOM,初始化变量

    在constructor中:

    1. 给 SliderWrapper、SliderList 和 SliderDot绑定ref:
    this.wrap = React.createRef()
    this.list = React.createRef()
    this.dot = React.createRef()
    
    1. 定义一些变量:
    this.dotLength = 0
    this.startPoint = {} //滑动开始的坐标
    this.distance = {} //滑动距离
    this.current = 0 //当前图片索引
    this.translatex = 0 
    this.startOffset = 0 //滑动开始坐标 相对于wrapper左边缘的偏移量
    this.imgWidth = this.props.imgWidth //一张图片的宽度
    this.threshold = 0.2 //滑动切换阈值
    this.touching = false //是否正在用手指滑动
    this.intervalID = null
    this.timerID = null
    

    在开始触摸滚动之前,将SliderList的内容复制一份,接在原来的DOM后面

    这个做法有一定缺陷:因为在列表渲染中,key值必须是唯一的,直接复制HTML会导致key值重复

    并设置第一个小圆点的样式为active:

    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')
    }
    

    编写处理滑动的函数

    1、让图片能够滚动起来

    核心思路如下:

    • 获取触点的坐标,计算滑动距离
    • 设置SliderList的 translateX 值
    handleTouchStart = (ev) => {
        let touch = ev.changedTouches[0]
        this.startPoint = {
          x: touch.pageX,
          y: touch.pageY
        }
     }
     
     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)`
    }
    

    2、切换图片

    我们需要判断什么时候切换到下一张:

    如果我们要切换到第n张图,那么 translateX 的值应该为:n * (-imgWidth)

    如果横向滑动距离为正,说明手指从左向右滑,应该切换到上一张

    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)`
    
    }
    

    到这里,我们已经可以拖动图片,然后切换上一张或下一张了

    3、无缝滚动

    滑动开始的这个瞬间

    如果我们发现 this.current 指向第一组的第一张,那么我们应该让它瞬间跳到第二组的第一张(保证它接下来可以向左或者向右滚动)

    同理,如果我们发现 this.current 指向第二组的最后一张,那么我们应该让它瞬间跳到第一组的最后一张

    怎么实现“瞬间”的跳动呢?只要设置 transition = 'none' 即可

    handleTouchStart = (ev) => {
        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)`
    }
    

    底部小圆点

    我们把这一个功能封装成一个辅助函数,每次触发touchend事件之后,就执行这个函数

    formatDots() {
        Array.from(this.dot.current.childNodes).forEach((item,index) => {
            item.classList.remove('active')
            if(index === (this.current%this.dotLength)){
            	item.classList.add('active')
            }
        })
    }
    

    参考资料:掘金-你不知道的轮播图细节

  • 相关阅读:
    Ajax基础:3.Json
    Head First Design Patterns State Pattern
    Head First Design Patterns Template Method Pattern
    Articles For CSS Related
    Head First Design Patterns Decorator Pattern
    代码审查工具
    How To Be More Active In A Group
    Head First Design Patterns Factory Method Pattern
    Head First Design Patterns Composite Pattern
    Tech Articles
  • 原文地址:https://www.cnblogs.com/baebae996/p/14386648.html
Copyright © 2020-2023  润新知