移动端h5使用touchstart,touchmove实现多图片拖拽效果

1、需求描述:对移动端多张图片可以进行拖动排序(例子中使用两排5张图片)
2、实现过程:
主要思想就是能够通过用户手势,捕捉到被拖 动元素以及拖动结束后的被交换元素,通过交换这两个元素实现拖动排序。除此之外,我们还需要获得被拖动元素在拖动过程中的的left和top值,增加拖动过程中的效果。

在这个过程中会用到移动端的手势事件touchstart,touchmove,touchend

首先,需要获得被拖动的目标图片,做法是首先计算出所有图片的一个左上角的left,top值数组,然后根据手势位置是否在[left,left+imgwidth]和[top,top+imgheight]之间来确定目标图片,并且赋值给一个初始值currentindex:

//计算全部图片的left和top
 getStartPos() {
        let doms = Array.prototype.slice.call(document.getElementsByTagName('li'))
        let centerpos = []
        doms.map(item => {
          centerpos.push({left:item.offsetLeft, top:item.offsetTop})
        })
        return centerpos
      }
//touchstart的事件监听函数
dragStart(ev) {
        let centerpos = this.getStartPos()
        let target = ev.target, self = this
        if(ev.changedTouches) {
          this.startX = ev.changedTouches[0].pageX;
          this.startY = ev.changedTouches[0].pageY;
        } else {
          this.startX = ev.clientX;
          this.startY = ev.clientY;
        }
        //鼠标边界性
        this.startX >this.clientWidth ? this.startX = this.clientWidth: null
        this.startY >this.clientHeight ? this.startY = this.clientHeight: null
    
        //偏移位置 = 鼠标的初始值 - 元素的offset
        this.disX = this.startX - target.offsetLeft
        this.disY = this.startY - target.offsetTop
        //判断用户拖动的是第几张图片
        for(let i=0; i

然后,就是取得移动过程中不断变化的left和top来实现图片移动的效果:

 dragMove(ev) {
        if(ev.changedTouches) {
          this.clientX = ev.changedTouches[0].pageX;
          this.clientY = ev.changedTouches[0].pageY;
        } else {
          this.clientX = ev.clientX;
          this.clientY = ev.clientY;
        }   
         //鼠标边界性
         this.clientX >this.clientWidth ? this.clientX = this.clientWidth: null
         this.clientY >this.clientHeight ? this.clientY = this.clientHeight: null
         // 元素位置 = 现在鼠标位置 - 元素的偏移值
         let left = this.clientX - this.disX;
         let top = this.clientY - this.disY;
          //边界
          if (left < 0) {
            left = 0;
          }
          if (top < 0) {
            top = 0;
          }
          if (left > this.clientWidth - this.elementWid) {  
            left = this.clientWidth - this.elementWid
          }
          if (top > this.clientHeight - this.elementHeight) {
            top = this.clientHeight - this.elementHeight;
          }
          this.setState({
            left: left,
            top: top
          })
          this.props.handletranstion(this.state.currentindex,left,top)
      }

其中handletranstion是调用该拖动组件的父组件传递过来的处理函数,通过这个函数可以在父组件中设置当前被拖动元素的left和top,以达到拖动过程中位置的变化

最后就是获得拖动结束,放置位置的图片编号,判断的方法同第一步中,也是采用放置的手势位置和元素的left和top等位置的判断:

 dragEnd(e) {
        //判断移动到哪个图片上方
        let self = this
        const {left,top} = this.state
        let centerpos = this.getStartPos()
        for(let i=0; i

同理,handleposition也是父组件的一个处理函数,用来把原始的图片List按照被拖动元素和放置元素进行交换,重新渲染UI。
完整的代码:

//父组件
import React from 'react'
import MobileTouch from './MobileTouch'
export default class MobileTouchExp extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            top:0,
            left:0,
            currentindex:10,
            imglist:[{imageUrl:'http://photos.tuchong.com/349669/f/6683901.jpg'},{imageUrl:'https://dimg06.c-ctrip.com/images/fd/tg/g4/M01/4A/BF/CggYHVXjzyeAOeymABuBTaHIgFc792.jpg'},{imageUrl:'https://dimg01.c-ctrip.com/images/fd/tg/g6/M01/7E/11/CggYs1cqtn2AIL7FACVmLZ72uhI947.jpg'},
            {imageUrl:'https://dimg04.c-ctrip.com/images/300v0x000000liuks9C9C_C_750_420_Q90.jpg'},{imageUrl:'https://dimg04.c-ctrip.com/images/300q0x000000l9393930E_C_750_420_Q90.jpg'}],
        }
    }

    handleposition (currentindex, targetindex){
       const { imglist } = this.state
       let newimglist = this.swapArray(imglist,currentindex,targetindex)
       this.setState({
        imglist:newimglist,
        left: 0,
        top:0,
        currentindex:10,
       })
    }

    //被拖动元素移动效果
    handletranstion (currentindex, left, top){
        this.setState({
            currentindex:currentindex,
            left:left,
            top:top
           })
    }


    swapArray (arr, index1, index2){
        arr[index1] = arr.splice(index2, 1, arr[index1])[0]
         return arr
     }
    render(){
        let { imglist, currentindex, left,top } = this.state
        return(
                
                    {
                        imglist && 
                        imglist.map((item,index) => (
                            
  • )) }
    ) } } //拖动组件 import React from 'react' import classnames from 'classnames' class MobileTouch extends React.Component { constructor(props) { super(props); this.elementWid = props.width || 100; this.elementHeight = props.height || 100; this.clientWidth = props.maxWidth; this.clientHeight = props.maxHeight; this._dragStart = this.dragStart.bind(this); this.state = { currentindex:0, targetindex:0, left: 0, top: 0 }; } dragStart(ev) { let centerpos = this.getStartPos() let target = ev.target, self = this if(ev.changedTouches) { this.startX = ev.changedTouches[0].pageX; this.startY = ev.changedTouches[0].pageY; } else { this.startX = ev.clientX; this.startY = ev.clientY; } //鼠标边界性 this.startX >this.clientWidth ? this.startX = this.clientWidth: null this.startY >this.clientHeight ? this.startY = this.clientHeight: null //偏移位置 = 鼠标的初始值 - 元素的offset this.disX = this.startX - target.offsetLeft this.disY = this.startY - target.offsetTop //判断用户拖动的是第几张图片 for(let i=0; ithis.clientWidth ? this.clientX = this.clientWidth: null this.clientY >this.clientHeight ? this.clientY = this.clientHeight: null // 元素位置 = 现在鼠标位置 - 元素的偏移值 let left = this.clientX - this.disX; let top = this.clientY - this.disY; //边界 if (left < 0) { left = 0; } if (top < 0) { top = 0; } if (left > this.clientWidth - this.elementWid) { left = this.clientWidth - this.elementWid } if (top > this.clientHeight - this.elementHeight) { top = this.clientHeight - this.elementHeight; } this.setState({ left: left, top: top }) this.props.handletranstion(this.state.currentindex,left,top) } dragEnd(e) { //判断移动到哪个图片上方 let self = this const {left,top} = this.state let centerpos = this.getStartPos() for(let i=0; i { centerpos.push({left:item.offsetLeft, top:item.offsetTop}) }) return centerpos } render() { const { maxWidth, maxHeight} = this.props; let styles = { width:maxWidth, height:maxHeight } const cls = classnames('moblie') return (
    {this.props.children}
    ) } } export default MobileTouch

    代码地址:https://github.com/kellywang1314/react_kw
    3、总结:目前对于end之后的位置判断过于呆板,不够灵敏,导致拖动效果不够好;需要继续改进,再者,组件的耦合程度过高,需要进一步解耦。

    你可能感兴趣的:(web前端,touchstart,touchmove,拖拽排序)