zoukankan      html  css  js  c++  java
  • 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')
            }
        })
    }
    

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

  • 相关阅读:
    IntelliJ IDEA使用心得之问题篇;
    IntelliJ IDEA使用心得之Maven项目篇
    IntelliJ IDEA使用心得之非Maven项目篇
    IntelliJ IDEA使用心得之插件篇
    IntelliJ IDEA使用心得之快捷键篇
    新博客地址
    【转载】Dijkstra算法和Floyd算法的正确性证明
    【转载】最小生成树之Kruskal算法
    论自动AC机
    【转载】C++ STL priority_queue用法
  • 原文地址:https://www.cnblogs.com/baebae996/p/14386648.html
Copyright © 2011-2022 走看看