• 售前

  • 售后

热门帖子
入门百科

Vue 可拖拽组件Vue Smooth DnD的使用详解

[复制链接]
伊索谗言 显示全部楼层 发表于 2021-8-14 14:59:46 |阅读模式 打印 上一主题 下一主题
目录


  • 简介和 Demo 展示
  • API: Container

    • 属性
    • 生命周期
    • 回调
    • 事件

  • API: Draggable

    • 实战


简介和 Demo 展示

最近需要有个拖拽列表的需求,发现一个简单好用的 Vue 可拖拽组件。安利一下~
Vue Smooth DnD 是一个快速、轻量级的拖放、可排序的 Vue.js 库,封装了 smooth-dnd 库。
Vue Smooth DnD 紧张包罗了两个组件,
  1. Container
复制代码
  1. Draggable
复制代码
  1. Container
复制代码
包罗可拖动的元素或组件,它的每一个子元素都应该被
  1. Draggable
复制代码
包裹。每一个要被设置为可拖动的元素都需要被
  1. Draggable
复制代码
包裹。
安装:
  1. npm i vue-smooth-dnd
复制代码
一个简单的 Demo ,展示组件的底子用法,实现了可以拖拽的列表。
  1. <template>
  2.     <div>
  3.         <div class="simple-page">
  4.             <Container @drop="onDrop">
  5.                 <Draggable v-for="item in items" :key="item.id">
  6.                     <div class="draggable-item">
  7.                         {{item.data}}
  8.                     </div>
  9.                 </Draggable>
  10.             </Container>
  11.         </div>
  12.     </div>
  13. </template>
  14. <script>
  15.     import { Container, Draggable } from "vue-smooth-dnd";
  16.     const applyDrag = (arr, dragResult) => {
  17.         const { removedIndex, addedIndex, payload } = dragResult
  18.         console.log(removedIndex, addedIndex, payload)
  19.         if (removedIndex === null && addedIndex === null) return arr
  20.         const result = [...arr]
  21.         let itemToAdd = payload
  22.         if (removedIndex !== null) {
  23.             itemToAdd = result.splice(removedIndex, 1)[0]
  24.         }
  25.         if (addedIndex !== null) {
  26.             result.splice(addedIndex, 0, itemToAdd)
  27.         }
  28.         return result
  29.     }
  30.     const generateItems = (count, creator) => {
  31.         const result = []
  32.         for (let i = 0; i < count; i++) {
  33.             result.push(creator(i))
  34.         }
  35.         return result
  36.     }
  37.     export default {
  38.         name: "Simple",
  39.         components: { Container, Draggable },
  40.         data() {
  41.             return {
  42.                 items: generateItems(50, i => ({ id: i, data: "Draggable " + i }))
  43.             };
  44.         },
  45.         methods: {
  46.             onDrop(dropResult) {
  47.                 this.items = applyDrag(this.items, dropResult);
  48.             }
  49.         }
  50.     };
  51. </script>
  52. <style>
  53.     .draggable-item {
  54.         height: 50px;
  55.         line-height: 50px;
  56.         text-align: center;
  57.         display: block;
  58.         background-color: #fff;
  59.         outline: 0;
  60.         border: 1px solid rgba(0, 0, 0, .125);
  61.         margin-bottom: 2px;
  62.         margin-top: 2px;
  63.         cursor: default;
  64.         user-select: none;
  65.     }
  66. </style>
复制代码
效果


API: Container


属性

            属性            类型            默认值            形貌                                    :orientation            string            vertical            容器的方向,可以为 horizontal 或 vertical                            :behaviour            string            move            形貌被拖动的元素被移动或复制到目标容器。 可以为 move 或 copy 或 drop-zone 或 contain 。move 可以在容器间相互移动,copy 是可以将元素复制到其他容器,但本容器内元素不可变,drop-zone 可以在容器间移动,但是容器内元素的序次是固定的。contain 只能在容器内移动。                            :tag            string, NodeDescription            div            容器的元素标签,默认是 div ,可以是字符串如 tag="table" 也可以是包罗 value和 props 属性的对象 :tag="{value: 'table', props: {class: 'my-table'}}"                            :group-name            string            undefined            可拖动元素可以在具有雷同组名的容器之间移动。如果未设置组名容器将不担当来自外部的元素。 这种行为可以被 shouldAcceptDrop 函数覆盖。 见下文。                            :lock-axis            string            undefined            锁定拖动的移动轴。可用值 x, y 或 undefined。                            :drag-handle-selector            string            undefined            用于指定可以开启拖拽的 CSS 选择器,如果不指定的话则元素内部恣意位置都可抓取。                            :non-drag-area-selector            string            undefined            克制拖动的 CSS 选择器,优先于 dragHandleSelector.                            :drag-begin-delay            number            0(触控装备为 200)            单位毫秒。体现点击元素持续多久后可以开始拖动。在此之前移动光标超过 5px 将取消拖动。                            :animation-duration            number            250            单位毫秒。体现放置元素和重新排序的动画持续时间。                            :auto-scroll-enabled            boolean            true            如果拖动项目靠近界限,第一个可滚动父项将自动滚动。(这个属性没看懂= =)                            :drag-class            string            undefined            元素被拖动中的添加的类(不会影响拖拽结束后元素的显示)。                            :drop-class            string            undefined            从拖拽元素被放置到被添加到页面过程中添加的类。                            :remove-on-drop-out            boolean            undefined            如果设置为 true,在被拖拽元素没有被放置到任何相关容器时,使用元素索引作为 removedIndex 调用 onDrop()                            :drop-placeholder            boolean,object            undefined            占位符的选项。包罗 className, animationDuration, showOnTop        
关于
  1. drag-class
复制代码
  1. drop-class
复制代码
  1. drop-placeholder.className
复制代码
的效果演示
  1. <Container # 省略其它属性...
  2.         :animation-duration="1000" # 放置元素后动画延时
  3.         drag-class="card-ghost"         
  4.         drop-class="card-ghost-drop"
  5.         :drop-placeholder="{
  6.             className: 'drop-preview',  # 占位符的样式
  7.             animationDuration: '1000', # 占位符的动画延迟
  8.             showOnTop: true            # 是否在其它元素的上面显示 设置为false会被其他的拖拽元素覆盖
  9.         }"
  10. >
  11.     <!-- 一些可拖拽元素 -->
  12.     <Draggable>....</Draggable>
  13. </Container>
复制代码
类对应样式
  1. .card-ghost {
  2.     transition: transform 0.18s ease;
  3.     transform: rotateZ(35deg);
  4.     background: red !important;
  5. }
  6. .card-ghost-drop {
  7.     transition: transform 1s cubic-bezier(0,1.43,.62,1.56);
  8.     transform: rotateZ(0deg);
  9.     background: green !important;
  10. }
  11. .drop-preview {
  12.     border: 1px dashed #abc;
  13.     margin: 5px;
  14.     background: yellow !important;
  15. }
复制代码
实际效果(我这良好的配色啊)


生命周期

一次拖动的生命周期通过一系列回调和事件举行形貌和控制,下面以包罗 3 个容器的示例为例举行阐明
(直接复制了文档没有翻译,API 详细解释可以看背面先容。):
  1. Mouse     Calls  Callback / Event       Parameters              Notes
  2. down   o                                                        Initial click
  3. move   o                                                        Initial drag
  4.        |
  5.        |         get-child-payload()    index                   Function should return payload
  6.        |
  7.        |   3 x   should-accept-drop()   srcOptions, payload     Fired for all containers
  8.        |
  9.        |   3 x   drag-start             dragResult              Fired for all containers
  10.        |
  11.        |         drag-enter
  12.        v
  13. move   o                                                        Drag over containers
  14.        |
  15.        |   n x   drag-leave                                     Fired as draggable leaves container
  16.        |   n x   drag-enter                                     Fired as draggable enters container
  17.        v
  18. up     o                                                        Finish drag
  19.                  should-animate-drop()  srcOptions, payload     Fires once for dropped container
  20.            3 x   drag-end               dragResult              Fired for all containers
  21.            n x   drop                   dropResult              Fired only for droppable containers
复制代码
请留意,应在每次
  1. drag-start
复制代码
之前和每次
  1. drag-end
复制代码
之前触发
  1. should-accept-drop
复制代码
,但为了清楚起见,此处已省略。
此中
  1. dragResult
复制代码
参数的格式:
  1. dragResult: {
  2.     payload,        # 负载 可以理解为用来记录被拖动的对象
  3.     isSource,       # 是否是被拖动的容器本身
  4.     willAcceptDrop, # 是否可以被放置
  5. }
复制代码
此中
  1. dropResult
复制代码
参数的格式:
  1. dropResult: {
  2.     addedIndex,     # 被放置的新添加元素的下标,没有则为 null
  3.     removedIndex,   # 将被移除的元素下标,没有则为 null
  4.     payload,        # 拖动的元素对象,可通过 getChildPayload 指定
  5.     droppedElement, # 放置的 DOM 元素
  6. }
复制代码
回调

回调在用户交互之前和期间提供了额外的逻辑和检查。
       
    1. get-child-payload(index)
    复制代码
    自界说传给
    1. onDrop()
    复制代码
    1. payload
    复制代码
    对象。   
    1. should-accept-drop(sourceContainerOptions, payload)
    复制代码
    用来确定容器是否可被放置,会覆盖
    1. group-name
    复制代码
    属性。   
    1. should-animate-drop(sourceContainerOptions, payload)
    复制代码
    返回
    1. false
    复制代码
    则阻止放置动画。   
    1. get-ghost-parent()
    复制代码
    返回幽灵元素(拖动时显示的元素)应该添加到的元素,默认是父元素,某些情况定位会出现标题,则可以选择自界说,如返回
    1. document.body
    复制代码


事件

       
    1. @drag-start
    复制代码
    在拖动开始时由所有容器发出的事件。参数
    1. dragResult
    复制代码
    。   
    1. @drag-end
    复制代码
    所有容器在拖动结束时调用的函数。 在
    1. @drop
    复制代码
    事件之前调用。参数
    1. dragResult
    复制代码
    。   
    1. @drag-enter
    复制代码
    每当拖动的项目在拖动时进入其界限时,相关容器要发出的事件。   
    1. @drag-leave
    复制代码
    每当拖动的项目在拖动时脱离其界限时,相关容器要发出的事件。   
    1. @drop-ready
    复制代码
    当容器中可能放置位置的索引发生变革时,被拖动的容器将调用的函数。根本上,每次容器中的可拖动对象滑动以打开拖动项目标空间时都会调用它。参数
    1. dropResult
    复制代码
    。   
    1. @drop
    复制代码
    放置结束时所有相关容器会发出的事件(放置动画结束后)。源容器和任何可以担当放置的容器都被以为是相关的。参数
    1. dropResult
    复制代码


API: Draggable
  1. tag
复制代码
同容器的
  1. tag
复制代码
指定可拖拽元素的 DOM 元素标签。

实战

实现一个简单的团队协作任务管理器。
  1. <template>
  2.     <div class="card-scene">
  3.         <Container
  4.                 orientation="horizontal"
  5.                 @drop="onColumnDrop($event)"
  6.                 drag-handle-selector=".column-drag-handle"
  7.         >
  8.             <Draggable v-for="column in taskColumnList" :key="column.name">
  9.                 <div class="card-container">
  10.                     <div class="card-column-header">
  11.                         <span class="column-drag-handle">&#x2630;</span>
  12.                         {{ column.name }}
  13.                     </div>
  14.                     <Container
  15.                             group-name="col"
  16.                             @drop="(e) => onCardDrop(column.id, e)"
  17.                             :get-child-payload="getCardPayload(column.id)"
  18.                             drag-class="card-ghost"
  19.                             drop-class="card-ghost-drop"
  20.                             :drop-placeholder="dropPlaceholderOptions"
  21.                             class="draggable-container"
  22.                     >
  23.                         <Draggable v-for="task in column.list" :key="task.id">
  24.                             <div class="task-card">
  25.                                 <div class="task-title">{{ task.name }}</div>
  26.                                 <div class="task-priority" :style="{ background: priorityMap[task.priority].color }">
  27.                                     {{ priorityMap[task.priority].label }}
  28.                                 </div>
  29.                             </div>
  30.                         </Draggable>
  31.                     </Container>
  32.                 </div>
  33.             </Draggable>
  34.         </Container>
  35.     </div>
  36. </template>
  37. <script>
  38.     import { Container, Draggable } from "vue-smooth-dnd";
  39.     const applyDrag = (arr, dragResult) => {
  40.         const { removedIndex, addedIndex, payload } = dragResult
  41.         console.log(removedIndex, addedIndex, payload)
  42.         if (removedIndex === null && addedIndex === null) return arr
  43.         const result = [...arr]
  44.         let itemToAdd = payload
  45.         if (removedIndex !== null) {
  46.             itemToAdd = result.splice(removedIndex, 1)[0]
  47.         }
  48.         if (addedIndex !== null) {
  49.             result.splice(addedIndex, 0, itemToAdd)
  50.         }
  51.         return result
  52.     }
  53.     const taskList = [
  54.         {
  55.             name: '首页',
  56.             priority: 'P1',
  57.             status: '待开发',
  58.             id: 1,
  59.         },
  60.         {
  61.             name: '流程图开发',
  62.             priority: 'P3',
  63.             status: '待评审',
  64.             id: 2,
  65.         },
  66.         {
  67.             name: '统计图展示',
  68.             priority: 'P0',
  69.             status: '开发中',
  70.             id: 3,
  71.         },
  72.         {
  73.             name: '文件管理',
  74.             priority: 'P1',
  75.             status: '开发中',
  76.             id: 4,
  77.         }
  78.     ]
  79.     const statusList = ['待评审', '待开发', '开发中', '已完成']
  80.     const taskColumnList = statusList.map((status, index) => {
  81.         return {
  82.             name: status,
  83.             list: taskList.filter(item => item.status === status),
  84.             id: index
  85.         }
  86.     })
  87.     const priorityMap = {
  88.         'P0': {
  89.             label: '最高优',
  90.             color: '#ff5454',
  91.         },
  92.         'P1': {
  93.             label: '高优',
  94.             color: '#ff9a00',
  95.         },
  96.         'P2': {
  97.             label: '中等',
  98.             color: '#ffd139',
  99.         },
  100.         'P3': {
  101.             label: '较低',
  102.             color: '#1ac7b5',
  103.         },
  104.     }
  105.     export default {
  106.         name: 'Cards',
  107.         components: {Container, Draggable},
  108.         data () {
  109.             return {
  110.                 taskColumnList,
  111.                 priorityMap,
  112.                 dropPlaceholderOptions: {
  113.                     className: 'drop-preview',
  114.                     animationDuration: '150',
  115.                     showOnTop: true
  116.                 }
  117.             }
  118.         },
  119.         methods: {
  120.             onColumnDrop (dropResult) {
  121.                 this.taskColumnList = applyDrag(this.taskColumnList, dropResult)
  122.             },
  123.             onCardDrop (columnId, dropResult) {
  124.                 let { removedIndex, addedIndex, payload } = dropResult
  125.                 if (removedIndex !== null || addedIndex !== null) {
  126.                     const column = taskColumnList.find(p => p.id === columnId)
  127.                     if (addedIndex !== null && payload) { // 更新任务状态
  128.                         dropResult.payload = {
  129.                             ...payload,
  130.                             status: column.name,
  131.                         }
  132.                     }
  133.                     column.list = applyDrag(column.list, dropResult)
  134.                 }
  135.             },
  136.             getCardPayload (columnId) {
  137.                 return index =>
  138.                     this.taskColumnList.find(p => p.id === columnId).list[index]
  139.             },
  140.         }
  141.     }
  142. </script>
  143. <style>
  144.     * {
  145.         margin: 0;
  146.         padding: 0;
  147.         font-family: 'Microsoft YaHei','PingFang SC','Helvetica Neue',Helvetica,sans-serif;
  148.         line-height: 1.45;
  149.         color: rgba(0,0,0,.65);
  150.     }
  151.     .card-scene {
  152.         user-select: none;
  153.         display: flex;
  154.         height: 100%;
  155.         margin: 20px;
  156.     }
  157.     .card-container {
  158.         display: flex;
  159.         flex-direction: column;
  160.         width: 260px;
  161.         min-width: 260px;
  162.         border-radius: 12px;
  163.         background-color: #edeff2;
  164.         margin-right: 16px;
  165.         height: calc(100vh - 40px);
  166.     }
  167.     .card-column-header {
  168.         display: flex;
  169.         height: 50px;
  170.         margin: 0 16px;
  171.         align-items: center;
  172.         flex-shrink: 0;
  173.         font-weight: 500;
  174.         font-size: 16px;
  175.     }
  176.     .draggable-container {
  177.         flex-grow: 1;
  178.         overflow: auto;
  179.     }
  180.     .column-drag-handle {
  181.         cursor: move;
  182.         padding: 5px;
  183.     }
  184.     .task-card {
  185.         margin: 10px;
  186.         background-color: white;
  187.         padding: 15px 10px;
  188.         border-radius: 8px;
  189.         box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.12);
  190.         cursor: pointer;
  191.         display: flex;
  192.         justify-content: space-between;
  193.     }
  194.     .task-title {
  195.         color: #333333;
  196.         font-size: 14px;
  197.     }
  198.     .task-priority {
  199.         width: 60px;
  200.         line-height: 20px;
  201.         border-radius: 12px;
  202.         text-align: center;
  203.         color: #fff;
  204.         font-size: 12px;
  205.     }
  206.     .card-ghost {
  207.         transition: transform 0.18s ease;
  208.         transform: rotateZ(5deg)
  209.     }
  210.     .card-ghost-drop {
  211.         transition: transform 0.18s ease-in-out;
  212.         transform: rotateZ(0deg)
  213.     }
  214.     .drop-preview {
  215.         background-color: rgba(150, 150, 200, 0.1);
  216.         border: 1px dashed #abc;
  217.         margin: 5px;
  218.     }
  219. </style>
复制代码
效果

到此这篇关于Vue 可拖拽组件Vue Smooth DnD的使用详解的文章就先容到这了,更多相关Vue 可拖拽组件内容请搜索脚本之家以前的文章或继承欣赏下面的相关文章盼望各人以后多多支持脚本之家!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作