阅读 258

VUE3.0实现简易的可拖放列表排序

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

项目地址

项目地址👉👉点击进入,可以直接设置为浏览器主页或者桌面快捷方式进行使用,本人在用,长期维护。

源码地址

完全开源,大家可以随意研究,二次开发。当然还是十分欢迎大家点个Star⭐⭐⭐
👉👉源码链接(gitee)       👉👉源码链接(github)

需求介绍

上面是我一个开源的用来收藏网站的小项目,但是现阶段只能一个一个的按顺序添加网址,这样就产生了一个问题,那就是后添加的一定在下面,而我如果新添加了一个比较常用的网站,而列表又过长的话,每次进入都需要翻到下面去找,实在是太不方便。

所以我就想添加一个拖拽排序的功能,在编辑模式下,可以通过拖拽图标进行排序,退出编辑模式自动保存,这样就解决了上面的问题,优化了用户体验。

下面就详细记录一下此功能的实现。

HTML拖放接口

首先还是先学习一下API

官方介绍

HTML 拖放(Drag and Drop)接口使应用程序能够在浏览器中使用拖放功能。例如,用户可使用鼠标选择可拖拽(draggable)元素,将元素拖拽到可放置(droppable)元素,并释放鼠标按钮以放置这些元素。拖拽操作期间,会有一个可拖拽元素的半透明快照跟随着鼠标指针。

拖拽事件

可用的拖拽事件一共有七个,其中三个是用于拖拽元素

  • dragstart 在元素开始被拖动时触发
  • dragend 在拖动操作完成时触发
  • drag 在元素被拖动时触发

四个是用于释放区域

  • dragenter 当被拖动元素进入到释放区所占据的屏幕空间时触发
  • dragover 当被拖动元素在释放区内移动时触发
  • dragleave 当被拖动元素没有放下就离开释放区时触发
  • drop 当被拖动元素在释放区里放下时触发

步骤

  • 首先给被拖放的元素添加 draggable 属性并添加 dragstart 事件处理函数
  • 定义拖放数据,本例定义的是拖拽元素的id
  • 定义一个释放区域,添加 dropdragover 事件处理函数,并阻止默认事件
  • 处理拖放数据
  • 拖放结束,添加 dragend 事件处理函数

例子

16 (1).gif

代码

<body>
  <div class="box" id='box1'>
    <div id="child" draggable="true"></div>
  </div>
  <div class="box" id='box2'>
  </div>
</body>
<script>
  ((doc) => {
    const box1 = doc.querySelector("#box1")
    const box2 = doc.querySelector("#box2")
    const child = doc.querySelector("#child")
    // 绑定事件函数
    const bindEvents = () => {
      child.addEventListener("drag", handleDrag)
      child.addEventListener("dragstart", handleDragstart)
      child.addEventListener("dragend", handleDragend)
      box2.addEventListener("dragenter", handleDragenter)
      box2.addEventListener("dragover", handleDragover)
      box2.addEventListener("dragleave", handleDragleave)
      box2.addEventListener("drop", handleDrop)
    };
    // 在元素被拖动时触发
    const handleDrag = (ev) => {
      console.log('🚀🚀 ~ drag  触发啦');
    }
    // 在元素开始被拖动时触发
    const handleDragstart = (ev) => {
      console.log('🚀🚀 ~ dragstart  触发啦');
      ev.dataTransfer.setData('id', ev.target.id);
    }
    // 在拖动操作完成时触发
    const handleDragend = (ev) => {
      console.log('🚀🚀 ~ dragend  触发啦');
    }
    // 当被拖动元素进入到释放区所占据的屏幕空间时触发
    const handleDragenter = (ev) => {
      console.log('🚀🚀 ~ dragenter  触发啦');
    }
    // 当被拖动元素在释放区内移动时触发
    const handleDragover = (ev) => {
      ev.preventDefault();
      console.log('🚀🚀 ~ dragover  触发啦');
    }
    // 当被拖动元素没有放下就离开释放区时触发
    const handleDragleave = (ev) => {
      console.log('🚀🚀 ~ dragleave  触发啦');
    }
    // 当被拖动元素在释放区里放下时触发
    const handleDrop = (ev) => {
      console.log('🚀🚀 ~ drop  触发啦');
      ev.preventDefault();
      const data = ev.dataTransfer.getData("id");
      ev.target.appendChild(document.getElementById(data));
    }

    // 初始化函数
    const init = () => {
      bindEvents();
    };
    // 执行初始化函数
    init();
  })(document);
</script>
复制代码

在VUE3中的实现思路

原生js实现拖拽排序我还没有弄,但是在vue中就非常的简单,因为我们在触发任何事件的时候,都可以拿到元素的index,我们可以靠index轻易实现。

  • dragstart中记录下旧的索引
  • dragover中记录下新的索引,每次经过一个都会更新
  • drop事件中处理数组,删掉旧的元素,在目标索引添加新的元素
//简略后的伪代码  详情请查看源码
<div draggable="true" v-for='(item,index) in markList' :key='index' 
@dragstart="handleDragstart(index)" 
@drop.prevent="handleDrop()"
@dragover.prevent="handleDragover(index)">
</div>


//js
const state = reactive<MarkState>({
  // 当前正在拖拽的元素索引
  oldItemIndex: -1,
  // 将插入的目标位置索引
  newItemIndex: -1,
});
// 开始拖拽时触发
const handleDragstart = (index: number) => {
  state.oldItemIndex = index;
};
// 停止拖拽时触发
const handleDrop = () => {
  // 如果位置没有发生改变 什么也不做
  if (state.newItemIndex === state.oldItemIndex) {
    return;
  }
  // 如果位置发生了改变
  emit('change-mark-index', state.oldItemIndex, state.newItemIndex);
};
// 拖拽经过其他元素时触发
const handleDragover = (index: number) => {
  state.newItemIndex = index;
};


//change-mark-index
// 修改书签索引
const changeMarkIndex = (oldItemIndex: number, newItemIndex: number) => {
  // 删除老的
  const changeItem = marks.value.splice(oldItemIndex, 1)[0];
  // 在列表中目标位置增加新的
  marks.value.splice(newItemIndex, 0, changeItem);
};
复制代码

最终实现

16.gif

接下来的开发计划

💡拖拽到目标位置后会有位置提示

💡支持拖拽到其他标签下

💡添加拖拽动画

拖拽动画这里我试了vue内部的 transition-group 但是没有成功,如果大家有什么思路或者建议,请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧

链接整合

🔊项目预览地址(GitHub Pages):👉👉alanhzw.github.io

🔊项目预览备用地址(自己的服务器):👉👉warbler.duwanyu.com

🔊源码地址(gitee):👉👉gitee.com/hzw_0174/wa…

🔊源码地址(github):👉👉github.com/alanhzw/War…

🔊我的博客:👉👉www.duwanyu.com

文章分类
前端
文章标签