拖拽元素组件的实现

140 阅读6分钟

拖拽元素组件的实现

1. 简单拖拽元素组件实现

<template>
  <div
    ref="box"
    class="draggable-box"
    :style="{ top: boxTop + 'px', left: boxLeft + 'px' }"
    @mousedown="startDrag"
  >
    Drag Me
  </div>
</template>

<script>
export default {
  name: 'DraggableIndex',
  data () {
    return {
      dragging: false,
      startX: 0,
      startY: 0,
      boxTop: 100,
      boxLeft: 100
    }
  },
  methods: {
    startDrag (event) {
      this.dragging = true
      this.startX = event.clientX
      this.startY = event.clientY
      window.addEventListener('mousemove', this.drag)
      window.addEventListener('mouseup', this.endDrag)
    },
    drag (event) {
      if (this.dragging) {
        const offsetX = event.clientX - this.startX
        const offsetY = event.clientY - this.startY
        this.boxTop += offsetY
        this.boxLeft += offsetX
        this.startX = event.clientX
        this.startY = event.clientY
      }
    },
    endDrag () {
      this.dragging = false
      window.removeEventListener('mousemove', this.drag)
      window.removeEventListener('mouseup', this.endDrag)
    }
  }
}
</script>

<style>
.draggable-box {
  position: absolute;
  background-color: #e0e0e0;
  padding: 10px;
  cursor: pointer;
}
</style>

2. 吸附边缘的拖拽元素组件实现

<template>
  <div
    ref="box"
    class="draggable-box"
    :style="{ top: boxTop + 'px', left: boxLeft + 'px' }"
    @mousedown="startDrag"
  >
    Drag Me
  </div>
</template>

<script>
export default {
  name: 'draggableIndex',
  data () {
    return {
      dragging: false,
      startX: 0,
      startY: 0,
      boxTop: 100,
      boxLeft: 100,
      windowWidth: 0,
      windowHeight: 0,
      boxWidth: 0,
      boxHeight: 0,
      edgeThreshold: 20 // 边缘吸附阈值
    }
  },
  mounted () {
    this.windowWidth = window.innerWidth
    this.windowHeight = window.innerHeight
    this.boxWidth = this.$refs.box.offsetWidth
    this.boxHeight = this.$refs.box.offsetHeight
  },
  methods: {
    startDrag (event) {
      this.dragging = true
      this.startX = event.clientX
      this.startY = event.clientY
      window.addEventListener('mousemove', this.drag)
      window.addEventListener('mouseup', this.endDrag)
    },
    drag (event) {
      if (this.dragging) {
        const offsetX = event.clientX - this.startX
        const offsetY = event.clientY - this.startY
        this.boxTop += offsetY
        if (this.boxTop <= 0) {
          this.boxTop = 0
        } else if (this.boxTop >= this.windowHeight - this.boxHeight) {
          this.boxTop = this.windowHeight - this.boxHeight
        }

        this.boxLeft += offsetX
        if (this.boxLeft <= 0) {
          this.boxLeft = 0
        } else if (this.boxLeft >= this.windowWidth - this.boxWidth) {
          this.boxLeft = this.windowWidth - this.boxWidth
        }

        this.startX = event.clientX
        this.startY = event.clientY
      }
    },
    endDrag () {
      this.dragging = false
      window.removeEventListener('mousemove', this.drag)
      window.removeEventListener('mouseup', this.endDrag)
    }
  }
}
</script>

<style>
.draggable-box {
  position: absolute;
  background-color: #e0e0e0;
  padding: 10px;
  cursor: pointer;
}
</style>

3. 实现可拖拽缩放的元素

截屏2024-01-14 下午7.07.57.png

步骤一:布局实现

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <meta charset="UTF-8" />  
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
    <title>Document</title>  
    <link rel="stylesheet" href="./index.css" />  
  </head>  
  <body>  
    <div class="box" id="drag">  
      <div class="resize-handle top-left"></div>  
      <div class="resize-handle top"></div>  
      <div class="resize-handle top-right"></div>  
      <div class="resize-handle right"></div>  
      <div class="resize-handle bottom-right"></div>  
      <div class="resize-handle bottom"></div>  
      <div class="resize-handle bottom-left"></div>  
      <div class="resize-handle left"></div>  
    </div>  
  </body>  
  <script src="./index.js"></script>  
</html>

步骤二:样式添加

.box {  
  position: absolute;  
  left50%;  
  top50%;  
  transformtranslate(-50%, -50%);  
  width200px;  
  height200px;  
  background-color#f0f0f0;  
  cursor: move;  
}  
  
.resize-handle {  
  position: absolute;  
  width10px;  
  height10px;  
  background-color#000;  
}  
  
.top-left {  
  top: -5px;  
  left: -5px;  
  cursor: nw-resize;  
}  
  
.top {  
  top: -5px;  
  leftcalc(50% - 5px);  
  cursor: ns-resize;  
}  
  
.top-right {  
  top: -5px;  
  right: -5px;  
  cursor: ne-resize;  
}  
  
.right {  
  topcalc(50% - 5px);  
  right: -5px;  
  cursor: ew-resize;  
}  
  
.bottom-right {  
  bottom: -5px;  
  right: -5px;  
  cursor: se-resize;  
}  
  
.bottom {  
  bottom: -5px;  
  leftcalc(50% - 5px);  
  cursor: ns-resize;  
}  
  
.bottom-left {  
  bottom: -5px;  
  left: -5px;  
  cursor: sw-resize;  
}  
  
.left {  
  topcalc(50% - 5px);  
  left: -5px;  
  cursor: ew-resize;  
}

步骤三:拖拽和缩放功能逻辑实现

const dragElement = document.getElementById("drag");  
// 拖拽功能  
dragElement.addEventListener("mousedown", startDrag);  
function startDrag(event) {  
  event.preventDefault();  
  const currentHandle = event.target;  
  const isResizeHandle = currentHandle.className.includes("resize-handle");  
  if (isResizeHandle) return;  
  const startX = event.clientX;  
  const startY = event.clientY;  
  const startLeft = dragElement.offsetLeft;  
  const startTop = dragElement.offsetTop;  
  document.addEventListener("mousemove", drag);  
  document.addEventListener("mouseup", stopDrag);  
  function drag(event) {  
    const dx = event.clientX - startX;  
    const dy = event.clientY - startY;  
    const newLeft = startLeft + dx;  
    const newTop = startTop + dy;  
    dragElement.style.left = newLeft + "px";  
    dragElement.style.top = newTop + "px";  
  }  
  function stopDrag() {  
    document.removeEventListener("mousemove", drag);  
    document.removeEventListener("mouseup", stopDrag);  
  }  
}  
// 缩放功能  
const resizeHandles = document.getElementsByClassName("resize-handle");  
Array.from(resizeHandles).forEach((handle) => {  
  handle.addEventListener("mousedown", startResize);  
});  
  
function startResize(event) {  
  event.preventDefault();  
  const currentHandle = event.target;  
  const direction = currentHandle.className.split(" ")[1];  
  const startX = event.clientX;  
  const startY = event.clientY;  
  const startWidth = dragElement.offsetWidth;  
  const startHeight = dragElement.offsetHeight;  
  const startLeft = dragElement.offsetLeft;  
  const startTop = dragElement.offsetTop;  
  document.addEventListener("mousemove", resize);  
  document.addEventListener("mouseup", stopResize);  
  function resize(event) {  
    const dx = event.clientX - startX;  
    const dy = event.clientY - startY;  
    let width = startWidth,  
      height = startHeight,  
      left = startLeft,  
      top = startTop;  
    if (direction.includes("left")) {  
      width = startWidth - dx + "px";  
      left = startLeft + dx / 2 + "px";  
    }  
    if (direction.includes("right")) {  
      width = startWidth + dx + "px";  
      left = startLeft + dx / 2 + "px";  
    }  
    if (direction.includes("top")) {  
      height = startHeight - dy + "px";  
      top = startTop + dy / 2 + "px";  
    }  
    if (direction.includes("bottom")) {  
      height = startHeight + dy + "px";  
      top = startTop + dy / 2 + "px";  
    }  
    if (parseInt(width) <= 0 || parseInt(height) <= 0return;  
    dragElement.style.width = width;  
    dragElement.style.height = height;  
    dragElement.style.left = left;  
    dragElement.style.top = top;  
  }  
  function stopResize() {  
    document.removeEventListener("mousemove", resize);  
    document.removeEventListener("mouseup", stopResize);  
  }  
}

步骤四:拖拽和缩放功能实现思路

拖拽功能

我们需要鼠标在div内按下不松开的时候,div可以跟着鼠标的位置移动,这里我们可以从鼠标的三种事件的触发来入手:

  • 1、鼠标按下(mousedown)

首先我们需要监听鼠标按下事件。

const dragElement = document.getElementById("drag");
dragElement.addEventListener("mousedown", startDrag);

判断点击事件是否为锚点触发,是的话则不执行拖拽逻辑。这里我们可以通过样式名来判断,锚点我们都给它加上了resize-handle,我们只需要判断样式名是否包含resize-handle就可以。

function startDrag(event) {
  event.preventDefault();
  const currentHandle = event.target;
  const isResizeHandle = currentHandle.className.includes("resize-handle");
  if (isResizeHandle) return;
}

记录下鼠标点击的坐标及拖拽元素所在位置,用于后面计算鼠标移动距离和对拖拽元素进行移动,监听鼠标移动和抬起事件

  const startX = event.clientX;
  const startY = event.clientY;
  const startLeft = dragElement.offsetLeft;
  const startTop = dragElement.offsetTop;
  document.addEventListener("mousemove", drag);
  document.addEventListener("mouseup", stopDrag);
  • 2、鼠标移动(mousemove)

鼠标移动的时候计算鼠标当前位置与鼠标点击位置的相对距离,将拖拽元素的位置也移动相应的相对距离

function drag(event) {
  const dx = event.clientX - startX;
  const dy = event.clientY - startY;
  const newLeft = startLeft + dx;
  const newTop = startTop + dy;
  dragElement.style.left = newLeft + "px";
  dragElement.style.top = newTop + "px";
}
  • 3、鼠标抬起(mouseup)

鼠标抬起的时候将鼠标移动和鼠标抬起的监听事件移除。

function stopDrag() {
  document.removeEventListener("mousemove", drag);
  document.removeEventListener("mouseup", stopDrag);
}

到这里我们就完成了一个最基本的可拖拽元素了,接下来就该来实现缩放功能了。

缩放功能

我们希望在点击缩放锚点进行移动的时候,元素会随着鼠标继续缩小或放大,这里我们仍然是要从鼠标触发的三种事件来入手:

  • 1、鼠标按下(mousedown)

我们需要监听所有锚点的鼠标按下事件,首先我们需要先获取所有的锚点元素:

const resizeHandles = document.getElementsByClassName("resize-handle");

再监听所有锚点元素的鼠标按下事件:

Array.from(resizeHandles).forEach((handle) => {
  handle.addEventListener("mousedown", startResize);
});

记录鼠标按下的初始位置及缩放元素的初始位置和宽高,便于后面对缩放元素进行缩放操作,并为缩放元素加上鼠标移动和鼠标抬起的监听事件。

function startResize(event) {
  event.preventDefault();
  const currentHandle = event.target;
  const direction = currentHandle.className.split(" ")[1];
  startX = event.clientX;
  startY = event.clientY;
  startWidth = dragElement.offsetWidth;
  startHeight = dragElement.offsetHeight;
  startLeft = dragElement.offsetLeft;
  startTop = dragElement.offsetTop;
  document.addEventListener("mousemove", resize);
  document.addEventListener("mouseup", stopResize);
  }
  • 2、鼠标移动(mousemove)

鼠标移动的时候我们需要判断当前是从哪个锚点触发的缩放事件,我们可以从锚点的样式名className来做区分。

const currentHandle = event.target;
const direction = currentHandle.className.split(" ")[1];

不同锚点我们需要对元素进行不同的操作,我们可以分为四个方位来进行判断并区分操作:

(1)左边

判断样式名是否包含left,左边锚点会触发元素的宽度变化及左右位置的变化,这时候鼠标左移相对于元素来说是放大,右移相对于元素来说是缩小,所以元素的宽度应该减去鼠标的位移距离。

if (direction.includes("left")) {
  width = startWidth - dx + "px";
  left = startLeft + dx / 2 + "px";
}

(2)右边

判断样式名是否包含right,右边锚点会触发元素的宽度变化及左右位置的变化,这时候鼠标左移相对于元素来说是缩小,右移相对于元素来说是放大,所以元素的宽度应该加上鼠标的位移距离。

if (direction.includes("right")) {
  width = startWidth + dx + "px";
  left = startLeft + dx / 2 + "px";
}

(3)上边

判断样式名是否包含top,上边锚点会触发元素的高度变化及上下位置的变化,这时候鼠标上移相对于元素来说是放大,下移相对于元素来说是缩小,所以元素的高度应该减去鼠标的位移距离。

if (direction.includes("top")) {
  height = startHeight - dy + "px";
  top = startTop + dy / 2 + "px";
}

(4)下边

判断样式名是否包含bottom,下边锚点会触发元素的高度变化及上下位置的变化,这时候鼠标上移相对于元素来说是缩小,下移相对于元素来说是放大,所以元素的高度应该加上鼠标的位移距离。

if (direction.includes("bottom")) {
  height = startHeight + dy + "px";
  top = startTop + dy / 2 + "px";
}

我们还需要判断当前元素的宽度和高度是否小于等于0,等于0的时候我们不再对其进行缩放操作,大于0则对元素进行赋值操作。

if (parseInt(width) <= 0 || parseInt(height) <= 0return;
dragElement.style.width = width;
dragElement.style.height = height;
dragElement.style.left = left;
dragElement.style.top = top;

完整代码如下:

function resize(event) {
    const dx = event.clientX - startX;
    const dy = event.clientY - startY;
    let width = startWidth,
      height = startHeight,
      left = startLeft,
      top = startTop;
    if (direction.includes("left")) {
      width = startWidth - dx + "px";
      left = startLeft + dx / 2 + "px";
    }
    if (direction.includes("right")) {
      width = startWidth + dx + "px";
      left = startLeft + dx / 2 + "px";
    }
    if (direction.includes("top")) {
      height = startHeight - dy + "px";
      top = startTop + dy / 2 + "px";
    }
    if (direction.includes("bottom")) {
      height = startHeight + dy + "px";
      top = startTop + dy / 2 + "px";
    }
    if (parseInt(width) <= 0 || parseInt(height) <= 0return;
    dragElement.style.width = width;
    dragElement.style.height = height;
    dragElement.style.left = left;
    dragElement.style.top = top;
}
  • 3、鼠标抬起(mouseup)

鼠标抬起的时候将鼠标移动和鼠标抬起的监听事件移除。

function stopResize() {
    document.removeEventListener("mousemove", resize);
    document.removeEventListener("mouseup", stopResize);
}

到这里我们就完成了一个最基本的可拖拽缩放的元素了。