• vue实现拖拽排序


            在业务中列表拖拽排序是比较常见的需求,常见的JS拖拽库有Sortable.js,Vue.Draggable等,大多数同学遇到这种需求也是更多的求助于这些JS库,其实,使用HTML原生的拖放事件来实现拖拽排序并不复杂,结合Vue的transition-group,还能快速的给排序添加过渡动画。

    HTML5拖放api

    1. 设置元素为可拖放

    为了使元素可拖动,把 draggable 属性设置为 true :

    <div draggable="true">能被拖放的元素</div>
    2. 拖放事件

    拖放涉及到两种元素,一种是被拖拽元素(源对象),一种是放置区元素(目标对象)。如下图所示,按住A元素往B元素拖拽,A元素即为源对象,B元素即为目标对象。

    触发对象事件名称说明
    在拖动目标上触发事件ondragstart用户开始拖动元素时触发
    ondrag元素正在拖动时触发
    ondragend用户完成元素拖动后触发
    释放目标时触发的事件ondragenter当被鼠标拖动的对象进入其容器范围内时触发此事件
    ondragover当某被拖动的对象在另一对象容器范围内拖动时触发此事件
    ondragleave当被鼠标拖动的对象离开其容器范围内时触发此事件
    ondrop当在一个拖动过程中,释放鼠标键时触发此事件

    需要注意的是:dragenterdragover事件的默认行为是拒绝接受任何被拖放的元素。因此,我们要在这两个拖放事件中使用preventDefault来阻止浏览器的默认行为;而且目标对象想要变成可释放区域,必须设置dragoverdrop 事件处理程序属性。

    基于vue的拖拽排序

    先不考虑排序动画,解释一下实现思路:

    • 由于拖动是实时的,所以没有使用drop而是使用了dragenter触发排序。
    • 在源对象开始被拖拽时记录其索引dragIndex,当它进入目标对象时(对应dragenter事件),将其插入到目标对象的位置。
    • 其中dragenter方法中有一个判断this.dragIndex !== index(index为当前目标对象的索引),这是因为源对象同时也是目标对象,当没有这个判断时,源对象开始被拖拽时就会立刻触发自身的dragenter事件,这是不合理的。

     

    有动画的拖拽排序

            把HTML的ul元素改为transition-group,在CSS中新增一个过渡transition: transform .3s;,就可以实现有动画的拖拽排序

    改造成可复用的组件

    1. <template>
    2. <transition-group name="drag" class="list" tag="ul">
    3. <li
    4. @dragstart="dragstart(index)"
    5. @dragenter="dragenter($event, index)"
    6. @dragend="dragend"
    7. @dragover.prevent
    8. :draggable="draggable"
    9. v-for="(item, index) in list"
    10. :key="item.id"
    11. class="list-item"
    12. >
    13. <slot :scope="item" :index="index"></slot>
    14. </li>
    15. </transition-group>
    16. </template>
    17. <script>
    18. export default {
    19. name: "DragList",
    20. model: {
    21. prop: "data",
    22. event: "change",
    23. },
    24. props: {
    25. // 唯一的key值是id
    26. data: {
    27. type: Array,
    28. default: () => [],
    29. },
    30. draggable: {
    31. type: Boolean,
    32. default: false,
    33. },
    34. },
    35. data() {
    36. return {
    37. dragIndex: "",
    38. };
    39. },
    40. computed: {
    41. list() {
    42. return [...this.data];
    43. },
    44. },
    45. methods: {
    46. // 拖拽元素(源对象)
    47. dragstart(index) {
    48. if (!this.draggable) return;
    49. this.dragIndex = index;
    50. },
    51. // 目标元素
    52. dragenter(e, index) {
    53. e.preventDefault();
    54. if (!this.draggable) return;
    55. // 避免源对象触发自身的dragenter事件
    56. if (this.dragIndex !== index) {
    57. const moving = this.list[this.dragIndex]; // 拖拽元素
    58. this.list.splice(this.dragIndex, 1); // 删除拖拽元素
    59. this.list.splice(index, 0, moving); // 在目标元素中追加拖拽元素
    60. // 排序变化后目标对象的索引变成源对象的索引
    61. this.dragIndex = index;
    62. this.$emit("change", this.list);
    63. }
    64. },
    65. dragover(e) {
    66. e.preventDefault();
    67. },
    68. dragend() {
    69. this.$emit("dragend");
    70. },
    71. },
    72. };
    73. </script>
    74. <style lang="less" scoped>
    75. .list {
    76. list-style: none;
    77. .drag-move {
    78. transition: transform 0.3s;
    79. }
    80. .list-item {
    81. // cursor: move;
    82. // width: 300px;
    83. // background: #ea6e59;
    84. // border-radius: 4px;
    85. // color: #fff;
    86. // margin-bottom: 6px;
    87. // height: 50px;
    88. // line-height: 50px;
    89. // text-align: center;
    90. }
    91. }
    92. </style>

  • 相关阅读:
    Git的概念和使用方法
    范围分区(暑假每日一题 39)
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    基于Hive的搜狗搜索日志与结果Python可视化设计
    提高接口自动化测试效率:使用 JMESPath 实现断言和数据提取
    贪心算法—Problem F
    音频基础 DAI:Digital Audio Interfaces
    c和cpp实现CPU核上绑定固定线程
    VMware安装Centos7及静态IP网络配置详细教程
    39、Spring AMQP
  • 原文地址:https://blog.csdn.net/hulinhulin/article/details/133691356