添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

使用vue-drag-drop实现拖放

2019.04 / Vue

vue-drag-drop是 HTML中Drag/Drop拖放接口 在Vue中的封装。

HTML元素拖放的基本原理是:

  • 鼠标选中一个可拖动的元素,移动鼠标到一个可放置的元素上,然后释放鼠标
  • 在拖动放置操作期间,会触发一系列事件,通过监听响应这些事件实现界面交互
  • 可拖动元素需添加属性draggable=”true”
  • 可放置元素需设置ondragover和ondrop响应事件
  • 在可拖动元素的ondragstart事件中,在dataTransfer属性上设置拖动的数据、图像和效果
  • 在可放置元素的ondrop事件中,获取放置上来的元素所携带的数据
  • 这里是Vue中使用vue-drag-drop实现的拖放,演示将诗词列表拖动到唐诗或宋词区域中实现分类收藏,借助Vuex在drag-panel和drop-panel组件之间共享拖放状态。

    拖动相关的store模块,dragType存放当前拖动元素的类型,是唐诗还是宋词。

    store/modules/drag.js
    import { DRAG } from '../types'

    export default {
    state: {
    dragType: null
    },

    getters: {
    dragType: state => state.dragType
    },

    mutations: {
    [DRAG.TYPE](state, dragType) {
    state.dragType = dragType
    }
    }
    }

    可拖动元素组件,拖动数据为诗词内容,开始拖动时设置dragType,拖动结束后清空dragType。通过slot设置了拖动时跟随鼠标的显示图像,相当于调用事件的dataTransfer.setDragImage方法。

    drag-panel.vue
    <template lang="pug">
    ul.list
    li(
    v-for="(item, index) in list"
    :key="index"
    )
    drag(
    :transfer-data="item"
    @dragstart="setDragType(item.type)"
    @dragend="setDragType(null)"
    )
    .drag-wrapper
    icon
    | {{ item.title }}
    div.drag-content(slot="image")
    | {{ item.title }}
    p {{ item.content }}
    </template>

    <script>
    import { mapMutations } from 'vuex'
    import { Drag } from 'vue-drag-drop'
    import Icon from './icon'

    export default {
    components: { Drag, Icon },

    props: {
    list: Array
    },

    methods: {
    ...mapMutations({
    setDragType: 'DRAG_TYPE'
    })
    // 除使用辅助函数映射,也可通过$store提交mutation
    // setDragType(type) {
    // this.$store.commit('DRAG_TYPE', type)
    // }
    }
    }
    </script>

    诗词的分类区域为可放置元素,当可拖动元素开始拖动时,与dragType相同的分类区域样式提示可放置,而其他分类区域则样式置灰,并通过@dragover事件将拖动效果dropEffect设为none。释放鼠标时,通过@drop事件获取诗词信息,保存收藏数据。

    drop-panel.vue
    <template lang="pug">
    .drop-container
    drop.drop-wrapper(
    v-for="(v, k) in collections"
    :key="k"
    :class="{ active: k === dragType, passive: dragType && k !== dragType }"
    @dragover="handleDragover(k, ...arguments)"
    @drop="handleDrop"
    )
    h2 {{ v.des }}收藏:
    ul
    li(
    v-for="(item, index) in v.list"
    :key="index"
    ) {{ item }}
    </template>

    <script>
    import { mapGetters, mapMutations } from 'vuex'
    import { Drop } from 'vue-drag-drop'

    export default {
    components: { Drop },

    computed: {
    ...mapGetters(['collections', 'dragType'])
    },

    methods: {
    ...mapMutations({
    addCollection: 'COLLECTIONS_ADD'
    }),

    handleDragover(type, data, event) {
    if (type !== data.type) {
    event.dataTransfer.dropEffect = 'none'
    }
    },

    handleDrop({ title }) {
    const list = this.collections[this.dragType].list
    if (list.indexOf(title) === -1) {
    this.addCollection({
    type: this.dragType,
    title
    })
    } else {
    alert('重复收藏')
    }
    }
    }
    }
    </script>

    主页面,组合拖动和放置组件,获取数据。

    App.vue
    <template lang="pug">
    .row
    .col.col-l
    drop-panel
    .col.col-r
    drag-panel(:list="list")
    </template>

    <script>
    import DragPanel from './drag-drop/drag-panel'
    import DropPanel from './drag-drop/drop-panel'
    import { list } from './drag-drop/list'

    export default {
    components: { DragPanel, DropPanel },

    data() {
    return {
    list: this._.shuffle(list)
    }
    }
    }
    </script>