Vue实现拖拽功能(vuedraggable / draggable)

2,061 阅读1分钟

1. 使用第三方组件库 vuedraggable

npm i -S vuedraggable

举一个多列拖拽的例子,剩下的属性和列子可以去链接文档查询,文档还是很全的。

// 多列拖拽
<template>
    <div class="drag">
        <div class="col-md-3">
            <draggable class="list-group" tag="div" v-model="list" v-bind="dragOptions" :move="onMove" @start="isDragging=true" @end="isDragging=false">
              <transition-group type="transition" :name="'flip-list'">
                <div class="item" v-for="element in list" :key="element.order">
                  <i :class="element.fixed? 'fa fa-anchor' : 'glyphicon glyphicon-pushpin'" @click=" element.fixed=! element.fixed" aria-hidden="true" />
                  {{element.name}}
                  <span class="badge">
                      {{element.order}}
                  </span>
                </div>
              </transition-group>
            </draggable>
        </div>
        
        <div class="col-md-3">
            <draggable element="span" v-model="list2" v-bind="dragOptions" :move="onMove">
              <transition-group name="no" class="list-group" tag="ul">
                <div class="item" v-for="element in list2" :key="element.order">
                  <i :class="element.fixed? 'fa fa-anchor' : 'glyphicon glyphicon-pushpin'" @click=" element.fixed=! element.fixed" aria-hidden="true"></i>
                  {{element.name}}
                  <span class="badge">{{element.order}}</span>
                </div>
              </transition-group>
            </draggable>
        </div>
    </div>
</template>

<script>
import draggable from "vuedraggable";

const message = [
  "vue.draggable",
  "draggable",
  "component",
  "for",
  "vue.js 2.0",
  "based",
  "on",
  "Sortablejs"
];

export default {
  components: {
    draggable
  },
  data() {
    return {
      list: message.map((name, index) => {
        return { name, order: index + 1, fixed: false };
      }),
      list2: [],
      editable: true,
      isDragging: false,
      delayedDragging: false
    };
  },
  computed: {
    dragOptions() {
      return {
        animation: 300,
        group: "description",
        disabled: !this.editable,
        ghostClass: "ghost"
      };
    }
  },
}

</script>

ps:vuedraggable这个包里引入了 element-ui,所以万一项目本身很大的话,还是不推荐使用了吧,不然打的包又要大很多。

2. 使用 draggable 属性 + transition-group

draggable:一个元素可以被拖放需要设置 draggable 属性为true(文本、图片和链接的draggable默认就是true)

<div draggable="true">能被拖放的元素</div>

设置了这个属性的元素就能实现拖拽效果了,但是实现拖拽并放置还不行,还需要处理配合其他方法,如下:

触发对象事件名称说明
源对象dragstart源对象开始被拖动时触发
drag源对象被拖动过程中反复触发
dragend源对象拖动结束时触发
目标对象dragstart源对象开始进入目标对象范围内触发
dragover源对象在目标对象范围内移动时触发
dragleave源对象离开目标对象范围时触发
drop源对象在目标对象范围内被释放时触发
<template>
  <div>
    // transition-group 使拖拽效果更加流畅
    <transition-group class="trans" name="trans-list" tag="div">
        // 遍历元素,给每个元素添加 draggable 属性
        <div class="item" v-for="(item,index) in apps" :key="item.order"  :draggable="item.fixed ? false : true" @dragstart="drag($event,index,item)" @dragend="dragend($event)" @dragenter="dragenter($event)" @dragleave="dragleave($event)" @dragover="allowDrop($event)" @drop="drop($event,index)">
          <span @click.stop.prevent="changeMove(item)">{{item.fixed ? '固定' : '可动'}}</span>
          {{item.title}}
        </div>
    </transition-group>
  </div>
</template>

<script>
import _ from 'lodash'
export default {
  name:'hardWorld',
  data(){
    return{
      apps:[
        {
          title:'空我',
          order:1
        },
        {
          title:'亚吉托',
          order:2
        },
        {
          title:'龙骑',
          order:3
        },
        {
          title:'555',
          order:4
        },
        {
          title:'剑',
          order:5
        },
        {
          title:'响鬼',
          order:6
        },
        {
          title:'甲斗',
          order:7
        },
        {
          title:'电王',
          order:8
        },
        {
          title:'月骑',
          order:9
        },
        {
          title:'帝骑',
          order:10
        },
      ],
      // 当前拖拽元素的下标
      indexS:null,
    }
  },
  methods:{
    // 修改元素 draggable 属性,false为不能拖拽
    changeMove(data){
      this.$set(data,'fixed',!data.fixed)
    },
    // 拖拽,保存当前拖拽元素的下标
    // 更改拖拽元素的透明度,使其与其他元素从视觉上区分开来
    drag(e,index,data){
      if(data.fixed){
        return
      }
      this.indexS = index;
      e.target.style.opacity = 0.5;
    },
    // 拖拽结束,拖拽元素的半透明度去掉,恢复原状
    dragend(e){
      e.target.style.opacity ='';
    },
    // 拖拽元素进入当前节点范围时,给目标元素添加一个背景色
    dragenter(e){
      if (e.target.className === 'item') {
        e.target.style.background = 'purple';
      }
    },
    // 拖拽元素离开当前节点范围时,去掉背景色
    dragleave(e){
      if (e.target.className === 'item') {
        e.target.style.background = '';
      }
    },
    // 拖拽元素在当前节点范围内移动时触发
    // 阻止目标节点触发默认操作
    allowDrop(e){
      e.preventDefault()
    },
    // 释放拖拽元素,获取目标元素的下标
    // 将拖拽元素原位置的值进行删除,并将其插入至新目标元素的位置
    drop(e,index){
      e.preventDefault();
      e.target.style.background = '';
      let moved=_.cloneDeep(this.apps[this.indexS])
      let moveTarget=_.cloneDeep(this.apps[index])
      if (index>-1 && index!==this.indexS && !moveTarget.fixed) {
        this.apps.splice(this.indexS,1)
        this.apps.splice(index,0,moved)
      }
    }
  }
}
</script>

<style scoped>
.trans{
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.trans-list-move{
  transition: transform .3s;
}

.item{
  width:19%;
  margin: 5px 0;
  background-color: #eee;
}
</style>

简单的拖拽demo就完成了,当然还能在这基础上实现更好的效果,可以随意发挥!