前端 · 2023年7月2日 0

实现拖拽组件

背景:

智慧养殖移动端表格页有添加功能,但是当列表很长时按钮会遮挡数据,如下图:

所以就需要支持拖动,同时需要一个吸边的效果:

拖拽原理

从一个位置移动到另一个位置,实时更改元素的位置 🤷

移动

<template>
  <view id="app">
    <view class="move-button" @touchmove="handleTouchMove" :style="[style]"></div>
  </view>
</template>
<script>
export default {
  name: 'App',
  data() {
    return {
      style: {},
    };
  },
  methods: {
    handleTouchMove(e) {
      console.log(e);
      const { clientX, clientY } = e.touches[0];
      this.style = {
        left: `${clientX}px`,
        top: `${clientY}px`,
      };
    },
  },
};
</script>
<style>
.move-button {
  position: absolute;
  top: 200px;
  right: 0;
  width: 200px;
  height: 200px;
  background-color: blue;
}
</style>

以上代码基本是可以移动元素,但是有个明显的问题,当开始移动的瞬间元素会跳动一下:

原因是:移动时获取的坐标赋值给的是元素的左上角坐标

那如何和处理?

我们只要设置的位置不是元素左上角,而是设置元素的拖动点就行。实际是不能设置元素内某个点的位置,我们需要变通下。我们发现拖动元素时,拖动点和元素的左上角的位置之间的差是不变的:

只要在拖动开始时,获得元素左上角的位置(posX,posY)以及拖动点的起始位置(startX,startY)然后 x,y 方向相减:

const diffX = startX - posX,
      diffY = startY - posY

获取元素的信息:

getElementInfo(selector){  
  let view = uni.createSelectorQuery().in(this).select(selector)  
  
  return new Promise((resolve, reject)=>{  
    view.boundingClientRect(data => {  
      resolve({  
        top:data.top,  
        right:data.right,  
        bottom:data.bottom,  
        left:data.left,  
        width:data.width,  
        height:data.height,  
      })  
    }).exec();  
  })  
  
}
handleTouchStart(e){
    this.elemInfo = await this.getElementInfo('.add-img')  
    const {clientX,clientY} = e.touches[0]  
    this.diffX = clientX - this.elemInfo.left  
    this.diffY = clientY - this.elemInfo.top
}

修改 handleTouchMove :

    handleTouchMove(e) {
      console.log(e);
      const { clientX, clientY } = e.touches[0];
      this.moveX = clientX - this.diffX
      this.moveY = clientY - this.diffY
      this.setButtonPosition()
    },
    setButtonPosition(){
        this.style = {  
            left:this.moveX + 'px',  
            top:this.moveY + 'px'  
        }  
    }

吸边

原理:当元素的中心点位置小于页面宽度一半时,吸附到左边,反之吸附到右边

拖动停止后,根据上述原理设置 x 的坐标:

suctionSide(){  
  const halfWidth = this.pageWidth/2  
  const elemCenterPointX = this.moveX + this.elemInfo.width/2  
  if(elemCenterPointX < halfWidth){  
    // 按钮中心点位置小于一半,吸附到左边  
    this.moveX = 0  
  }else{  
    // 按钮中心点位置大于一半,吸附到右边  
    this.moveX = this.pageWidth - this.elemInfo.width  
  }  
  this.setButtonPosition()  
}

获取页面宽度:

uni.getSystemInfo({  
  success:res=>{  
    console.log(res)  
    this.pageWidth = res.windowWidth  
  }  
})

未完待续…

参考:

  1. uni.createSelectorQuery() | uni-app官网