v-drag

简介

v-drag 指令用于处理根据鼠标拖拽来变换目标元素形态、位置等的场景。

示例

拖动元素

<template>
<article>
  <div
    ref="target"
    v-drag:target.translate
    class="target"
  />
</article>
</template>

<script>
import { drag } from 'veui'

export default {
  directives: {
    drag
  }
}
</script>

<style lang="less" scoped>
.target {
  width: 80px;
  height: 80px;
  background: #e7e7e7;
}
</style>

在指定元素区域内拖动

<template>
<article>
  <div
    ref="container"
    class="container"
  >
    <div
      ref="target"
      v-drag:target.translate="{containment: 'container'}"
      class="target"
    />
  </div>
</article>
</template>

<script>
import { drag } from 'veui'

export default {
  directives: {
    drag
  }
}
</script>

<style lang="less" scoped>
.target {
  width: 80px;
  height: 80px;
  background: #e7e7e7;
}

.container {
  width: 100%;
  height: 300px;
  background: #f7f7f7;
}
</style>

拖动多个元素

主元素
<template>
<article>
  <div
    ref="target1"
    v-drag:target1,target2,target3.translate
    class="target"
  >
    主元素
  </div>
  <div
    ref="target2"
    class="target"
  />
  <div
    ref="target3"
    class="target"
  />
</article>
</template>

<script>
import { drag } from 'veui'

export default {
  directives: {
    drag
  }
}
</script>

<style lang="less" scoped>
.target {
  width: 80px;
  height: 80px;
  background: #e7e7e7;
  display: inline-flex;
  margin-right: 40px;
  align-items: center;
  justify-content: center;
  vertical-align: top;
}

.container {
  width: 100%;
  height: 300px;
  background: #f7f7f7;
}
</style>

限制拖动方向

水平方向
垂直方向
<template>
<article>
  <div
    ref="target1"
    v-drag:target1.translate.x
    class="target"
  >
    水平方向
  </div>
  <div
    ref="target2"
    v-drag:target2.translate.y
    class="target"
  >
    垂直方向
  </div>
</article>
</template>

<script>
import { drag } from 'veui'

export default {
  directives: {
    drag
  }
}
</script>

<style lang="less" scoped>
article {
  height: 200px;
}

.target {
  width: 80px;
  height: 80px;
  background: #e7e7e7;
  display: inline-flex;
  margin-right: 40px;
  align-items: center;
  justify-content: center;
  vertical-align: top;
}

.container {
  width: 100%;
  height: 300px;
  background: #f7f7f7;
}
</style>

水平排序

Lorem
ipsum
dolor
sit
amet
consectetur
adipiscing
elit
sed
do
eiusmod
tempor
incididunt
ut
labore
et
dolore
magna
aliqua
<template>
<article>
  <transition-group
    ref="group"
    name="list"
    tag="div"
    class="items"
  >
    <div
      v-for="item in items"
      :key="item"
      v-drag.sort.x="{
        name: 'words',
        containment: 'group',
        sort: sortCallback,
      }"
      class="item"
    >
      {{ item }}
    </div>
  </transition-group>
  </section>
</article>
</template>

<script>
import { drag } from 'veui'

const items = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'.split(/[,. ]+/).filter(Boolean)

export default {
  directives: {
    drag
  },
  data () {
    return {
      items
    }
  },
  methods: {
    sortCallback (fromIndex, toIndex) {
      let items = this.items
      let item = items[fromIndex]
      items.splice(fromIndex, 1)
      items.splice(toIndex, 0, item)
    }
  }
}
</script>

<style lang="less" scoped>
.items {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.item {
  background: white;
  border: 1px solid pink;
  border-radius: 3px;
  margin: 0 10px 8px 0;
  padding: 1px 2px;
}

.list-move {
  transition: transform 200ms ease;
}
</style>

垂直排序

Lorem
ipsum
dolor
sit
amet
consectetur
adipiscing
elit
sed
do
eiusmod
tempor
incididunt
ut
labore
et
dolore
magna
aliqua
<template>
<article>
  <transition-group
    ref="group"
    name="list"
    tag="div"
    class="items"
  >
    <div
      v-for="item in items"
      :key="item"
      v-drag.sort.y="{
        name: 'words',
        containment: 'group',
        sort: sortCallback,
      }"
      class="item"
    >
      {{ item }}
    </div>
  </transition-group>
</article>
</template>

<script>
import { drag } from 'veui'

const items = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'.split(/[,. ]+/).filter(Boolean)

export default {
  directives: {
    drag
  },
  data () {
    return {
      items
    }
  },
  methods: {
    sortCallback (fromIndex, toIndex) {
      let items = this.items
      let item = items[fromIndex]
      items.splice(fromIndex, 1)
      items.splice(toIndex, 0, item)
    }
  }
}
</script>

<style lang="less" scoped>
.item {
  background: white;
  border: 1px solid pink;
  border-radius: 3px;
  margin: 0 10px 8px 0;
  padding: 1px 2px;
}

.items {
  padding: 0;
  list-style-position: inside;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 300px;
  resize: both;

  .item {
    width: 20%;
    border-color: peachpuff;
  }
}

.list-move {
  transition: transform 200ms ease;
}
</style>

API

绑定值

类型:Object

参数类型默认值描述
targetsArray<string | Vue | HTMLElement>[]

该参数指定了目标元素集合,在指令所在元素上拖拽鼠标的时候,会按照指定的方式变换所有目标元素。

类型描述
string在指令所在组件上下文中,根据 ref 查找指定的 DOM 元素集合。
Vue组件实例,直接使用 vm.$el 元素。
HTMLElementDOM 元素,直接使用。
typestring-该参数指定变化的类型,目前内置了 translate 类型(变换目标元素位置),可以进行扩展
containmentstring | Vue | HTMLElement | Object-

目标元素在变换的时候,应当始终位于 containment 所指定的区域内。

如果通过 containment 解析出来是一个 DOM 元素,那么所有目标元素就应当在该元素内变换;如果解析出来是一个矩形区域描述(相对于视口的 top、left、width、height),那么所有目标元素就应当在该矩形区域内变换。

类型描述
string如果以 @ 开头,就被认为是特殊逻辑,会透传给具体的 Handler 处理;否则,在指令所在组件上下文中,根据 ref 查找指定的 DOM 元素。
Vue根据组件实例找到 DOM 元素。
HTMLElement直接接收 DOM 元素。
Object接受任意包含 { top, left, width, height } 字段的普通对象,表示相对于视口的矩形区域的坐标和尺寸,四个字段均为 number 类型。
axisstring-限制所有目标元素只能在水平或者垂直方向上做变换。取值为:xy
dragstartfunction(): Objectfunction() {}

鼠标拖拽开始事件的回调函数。回调参数为 ({ event: DragEvent })

dragfunction(): Objectfunction() {}

鼠标拖拽中事件的回调函数。回调参数为 ({ event, distanceX, distanceY })

参数类型描述
eventDragEvent原生拖拽事件对象。
distanceXnumber水平方向自拖拽开始以后移动的总距离。
distanceYnumber垂直方向自拖拽开始以后移动的总距离。
disabledbooleanfalse该指令是否被禁用。
dragendfunction(): Objectfunction() {}鼠标拖拽结束事件的回调函数。回调参数同 drag
readyfunctionfunction() {}指令初始化完成的回调函数,会传出一个句柄对象参数,该对象上有一个 reset() 方法,用于将所有目标元素重置为变换之前的样子。

修饰符

对应 Object 绑定值中的 type / axis。例如:

<!-- 沿着垂直方向做位移变换 -->
<div v-drag.translate.y></div>

参数

对应 Object 绑定值中的 targets。值是一个用 , 分隔的、表示一到多个 ref 的字符串。例如:

<div v-drag:box1,box2></div>

拖拽排序(v-drag.sort)

可以通过 v-drag.sortv-drag="{ type: 'sort', ... }" 来实现拖拽排序。

绑定值

类型:Object

参数类型默认值描述
namestring-用来标记一组项目,在这组项目中进行排序。
typestring-该参数指定变化的类型,拖拽排序是 sort
containmentstring | Vue | HTMLElement-参见指令基础描述,对于拖拽排序功能不支持 Object 类型的值。
axisstring-限制所有目标元素只能在水平或者垂直方向上做排序。取值为:xy
sortfunction(fromIndex: number, toIndex: number): void-

排序指令仅仅通过该回调告诉用户排序的情况,即:从原来位置(fromIndex)移动到新位置(toIndex),需要自行据此对数据源进行更新。

handlestring | Vue | HTMLElement-指定拖拽排序的拖拽操作的“把手”元素,只有拖动对应元素才会触发排序。类型同 containment 参数。

扩展

可以通过继承 BaseHandler 扩展 v-drag 指令:

import BaseHandler from 'veui/directives/drag/BaseHandler'
import { registerHandler } from 'veui/directives/drag'

class RotateHandler extends BaseHandler { }

registerHandler('rotate', RotateHandler)

然后通过 type 参数使用 RotateHandler

<div v-drag="{ type: 'rotate' }"></div>
<!-- 或者 -->
<div v-drag.rotate></div>

BaseHandler

BaseHandler 中各成员的说明如下:

成员名类型描述
optionsObject解析出来的参数组成的对象。
contextVue指令所在的组件。
isDraggingboolean是否处于拖拽过程中。
startfunction(Object)绑定值中的 dragstart 字段。
dragfunction(Object)绑定值中的 drag 字段。
endfunction(Object)绑定值中的 dragend 字段。
destroyfunction()指令与 DOM 元素解绑的时候调用。
setOptionsfunction(options)设置参数。
resetfunction()重置变换。