likes
comments
collection
share

摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

作者站长头像
站长
· 阅读数 19

前言: 最近实现了一个需求:产品小姐姐要求我们的主页的应用可以拖拽到某个区域进行删除操作。

因为本身自己对移动端拖拽就十分感兴趣,所以也尝试着先不使用其他库,先尝试自己来解决这个需求,在实现过程中也对网页布局有了更深的理解。

今天想分享一下自己在实现这个需求时的一些思路,或许能帮你重新理解拖拽的原理🎁。


一. 效果预览

摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

二. 实现元素拖拽的思路

三. 判断两个元素是否相交

  1. 产品小姐姐在项目上的需求是:拖拽的时候,产生一片区域,当应用被 “拖进” 这个区域的时候,就需要提示用户是否要删除该应用。在这里我们就随手创建一个简单的区域来模拟这个场景。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  2. 那么我们的现在的主要目的就是如何通过代码判断出下面这种情况。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  3. 理清思路很重要,注意,我们准备在干什么?我们在把一个 “元素拖进另外一个元素中。” 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  4. 让我们把上面的场景给抽象化理解:我们是不是在制造两个矩形相交的场景? 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  5. 但两个矩形产生相交的场景很多,下面仅仅只是几个简单相交的场景,就包括了这么多判断因素,可见我们要考虑的也需要很多。我们想一想是否能进一步简化一下相交的这个场景呢? 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  6. 这里其实可以用逆向思维来判断这个场景,我们只需要考虑两个矩形不相交的情况,然后对这个条件取反不就能达到同样的目的吗?如下图所示(B 相对于 A):我不管 B 具体位于 A 的哪个具体方向,到底是左边偏上还是右边偏下这些情况我都不考虑,我关心的是只要 B 同时满足下面这四个条件,那么 BA 就绝对不可能相交。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

四. 判断两个元素不相交

  1. 首先是第一种场景 BA 的左边。这里我们至少需要知道两个坐标信息,那就是 AB左上角右下角的坐标信息,这里为了简化变量的书写,我们用 ltp 表示 (左-上)的点坐标信息rbp 表示 (右-下)的点坐标信息。(这里假设我们已经拿到了,稍后会解释为什么需要这两个信息) 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  2. 这里需要发挥一点点你的空间想象能力,Brbp.xB 矩形身上所有坐标点距离 A 最近的那一个点。如下图所示,我们假设 B 矩形产生了无数个点坐标,那么它最右侧边上的点就是此时距离 A 最近的。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  3. 那么相对的 Altp.x 是距离 B 最近的那个点。如果 B.rbp.x < A.ltp.x 那么 B 在这种情况下一定与 A 的左侧不可能产生相交。因为 AB 距离彼此最近的两个点都没有产生空间关系,(如果相交,则 B.rbpx 一定大于等于 A.ltp.x )那么别点就更不可能了。

  4. 继续用这种方法思考 BA 下边的情况。对于 B 来讲,距离 A 最近的点就是 B.ltp.y,相对应的 A 距离 B 最近的点是 A.rbp.y。假如说 AB 连这两个相距最近的点的关系是 B.ltp.y>A.rbp.y,那么 B 一定不和 A 位于下方相交。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  5. 相同的空间思维, BA 的上边的情况,如果 B.rbp.y < A.ltp.yB 一定不可能和 A 在上方产生相交。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  6. 同理,如果 BA 的右侧,如果此时相距彼此最近的 B.ltp.x > A.rbp.x, 那么 B 就不能和 A 在右侧相交。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  7. 希望你能明白这其中的空间位置关系,如果第一遍没看明白,你可能需要再读一遍。

五. 实现相交判断

  1. 读懂了上面不相交的情况判断,那么我们接下来的需求就是如何获得 ltprbp 对应的坐标信息了。

  2. 这里需要用到 getBoundingClientRect 这个 api,这个 api 会返回元素相对于视口的相对位置。

摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  1. 聪明的你可能已经在 MDN 看到了下面这个图,你也或许注意到了,这个函数的返回值 xy 恰好对应了我们需要的 ltp 的信息,bottomright 是我们需要的 rbp 的信息。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  2. 为了方便的进行下一步,我们分别给拖拽元素删除区域打上 ref 方便后面获取 dom 元素进行操作。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  3. 此时我们就需要另外几个变量来存放我们马上要用到的信息。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  4. 我们随手写一个获取坐标信息的函数,很简单的逻辑,不过多解释。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  5. 然后在 touchstart 里分别获取 dragInfodeleteInfo摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  6. touchmove 中添加新的逻辑,我们新增一个 moveDragInfo 变量来当作移动过程中 dragEl 的临时坐标信息,因为我们已经有了 diff 值,那么我们只需要每次移动的时候,将原始值 dragInfo 的坐标信息和 diff 相加即可,然后将结果赋值给 moveDragInfo摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  7. 有了实时的坐标信息,那么根据上文第四章节的内容,我们可以写出以下不相交的逻辑。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  8. 我们测试一下之前的猜想是否成立: 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

  9. 由上图可以很清楚的看到,我们已经可以正确判断出拖动元素什么时候和删除区域相交了。那么我们也就可以在相交的时候正确处理之后的逻辑了。至此这个需求的核心步骤就已经完成,剩下的只是完善处理业务上的需求交互逻辑,这里不再过多赘述。

六. 源码

<script lang="ts" setup>
import { ref, onMounted } from "vue";
const initInfo = {
 x: 0,
 y: 0,
};

const diff = {
 x: 0,
 y: 0,
};

const dragEl = ref<HTMLDivElement>();
const deleteArea = ref<HTMLDivElement>();

let deleteInfo = {
 //删除元素的坐标信息
 ltp: {
   x: 0,
   y: 0,
 },
 rbp: {
   x: 0,
   y: 0,
 },
};

let dragInfo = {
 //拖拽元素的坐标信息
 ltp: {
   x: 0,
   y: 0,
 },
 rbp: {
   x: 0,
   y: 0,
 },
};

let test = 0;

function touchStart(e: TouchEvent) {
 if (!dragEl.value || !deleteArea.value) return;

 const { clientX, clientY } = e.touches[0];
 initInfo.x = clientX;
 initInfo.y = clientY;
}

function touchMove(e: TouchEvent) {
 const { clientX, clientY } = e.touches[0];
 diff.x = clientX - initInfo.x;
 diff.y = clientY - initInfo.y;

 let moveDragInfo = { ltp: { x: 0, y: 0 }, rbp: { x: 0, y: 0 } };

 moveDragInfo.ltp.x = dragInfo.ltp.x + diff.x;
 moveDragInfo.ltp.y = dragInfo.ltp.y + diff.y;

 moveDragInfo.rbp.x = dragInfo.rbp.x + diff.x;
 moveDragInfo.rbp.y = dragInfo.rbp.y + diff.y;

 const notIntersected =
   moveDragInfo.rbp.x < deleteInfo.ltp.x || //左侧
   moveDragInfo.ltp.x > deleteInfo.rbp.x || //右侧
   moveDragInfo.ltp.y > deleteInfo.rbp.y || //下侧
   moveDragInfo.rbp.y < deleteInfo.ltp.y; //上侧

 if (!notIntersected) {
   console.log("相交了,提示删除");
 } else {
   console.log("无事发生");
 }

 const target = e.target as HTMLDivElement;
 target.style.transform = `translate(${diff.x}px,${diff.y}px)`;
}

function touchEnd(e: TouchEvent) {
 const target = e.target as HTMLDivElement;
 target.style.transform = `translate(0)`;
}

//获取元素的左上角和右下角坐标信息
function getElementInfo(element: HTMLDivElement) {
 const rectInfo = element.getBoundingClientRect();
 const { left, top, right, bottom } = rectInfo;
 return {
   element: element,
   ltp: {
     x: left,
     y: top,
   },
   rbp: {
     x: right,
     y: bottom,
   },
 };
}

onMounted(() => {
 dragInfo = getElementInfo(dragEl.value!);
 deleteInfo = getElementInfo(deleteArea.value!);
});
</script>

<template>
 <div class="w-full h-full bg-blue flex flex-col gap-230px">
   <div
     @touchstart="touchStart"
     @touchmove="touchMove"
     @touchend="touchEnd"
     class="w-100px h-100px mx-auto bg-black"
     ref="dragEl"
   ></div>

   <div ref="deleteArea" class="w-full leading-100px bg-red text-center">
     <span class="text-black text-20px">删除区域</span>
   </div>
 </div>
</template>

七. 结语

实现自由拖拽其实是这个功能最简单的部分,关键点在于如何判断两个元素相交,这里我们通过逆向思维,通过判断什么时候 “不相交” 来较为简单的得出相交的场景,从而实现了相交时的逻辑判断。

下图是我自己实现的一版拖拽排序,也用到了相同的判断逻辑。其实大部分的功能都已经实现了,可惜我没办法正确处理页面滚动时元素位置的重新获取,由于时间限制,最后无奈放弃而选用了 vue-drag-plus 库。不过我认为结果不是最重要的,重要的是自己尝试完成这个需求过程中学到的其他知识,丰富了我脑中的知识库,将来在完成别的需求时,我相信我会有不一样的思路。 摸鱼10分钟:快速读懂拖拽删除的原理最近实现了一个需求:产品小姐姐要求我们的应用桌面可以实现拖拽到某个区域进行删除操作。

转载自:https://juejin.cn/post/7391277159611645979
评论
请登录