您的位置:

Vue3拖拽组件详解

一、基本概念

拖拽组件是指用户可以用鼠标拖动DOM元素,并释放到指定位置的交互式UI组件。Vue3中新增了一组钩子,使得我们可以轻松地实现拖拽组件的逻辑,这些钩子包括:

  • onDragstart - 拖拽开始时触发
  • onDrag - 拖拽时触发
  • onDragend - 拖拽结束时触发
  • onDrop - 将一个元素拖拽到另一个元素时触发

在Vue3中,我们可以通过直接给DOM元素添加这些事件的监听函数来实现拖拽功能。

二、基本用法

下面是一个基本的拖拽组件的代码示例:

<template>
  <div
    class="drag-item"
    draggable="true"
    @dragstart="onDragStart"
    @drag="onDrag"
    @dragend="onDragEnd">
    Drag Me!
  </div>
</template>

<script>
export default {
  methods: {
    onDragStart(event) {
      event.dataTransfer.setData('text/plain', 'Drag Me!');
      event.target.style.opacity = '0.4';
    },
    onDrag(event) {
      event.target.style.left = `${event.clientX}px`;
      event.target.style.top = `${event.clientY}px`;
    },
    onDragEnd(event) {
      event.target.style.opacity = '';
    }
  }
};
</script>

<style scoped>
.drag-item {
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: #ccc;
  border-radius: 50%;
  cursor: move;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

在这个例子中,我们使用了HTML5提供的拖放API来实现拖拽功能,具体来说:

  • 元素的draggable属性设置为true,使它成为可拖拽元素
  • 元素上添加三个事件监听器,分别处理拖拽开始、拖拽过程、拖拽结束三个事件
  • 在onDragStart事件中,设置的drag data将会在onDrop事件中使用,这里我们使用的是文本数据类型
  • 在onDrag事件中,根据鼠标的坐标,实时更新
    的位置
  • 在onDragEnd事件中,还原
    的透明度

三、使用Vue3 Composition API实现拖拽组件

在Vue3中,我们可以使用Composition API重构我们的代码,使其更具可复用性和可维护性。下面是一个使用Composition API实现拖拽组件的示例:

<template>
  <div
    class="drag-item"
    ref="dragItem"
    v-draggable="{ dragData: 'Drag Me!', onDragStart, onDrag, onDragEnd }">
    Drag Me!
  </div>
</template>

<script>
import { ref } from 'vue';
import { useDraggable } from '@vueuse/core';

export default {
  setup() {
    const dragItem = ref(null);

    useDraggable(dragItem);

    const onDragStart = (event) => {
      event.target.style.opacity = '0.4';
    };

    const onDrag = (event) => {
      event.target.style.left = `${event.clientX}px`;
      event.target.style.top = `${event.clientY}px`;
    };

    const onDragEnd = (event) => {
      event.target.style.opacity = '';
    };

    return {
      dragItem,
      onDragStart,
      onDrag,
      onDragEnd,
    };
  },
};
</script>

<style scoped>
.drag-item {
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: #ccc;
  border-radius: 50%;
  cursor: move;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

在这个示例中,我们使用Vue3的Composition API和vueuse库来实现拖拽组件。具体来说:

  • 使用useDraggable hook来封装拖拽逻辑,并将拖拽组件的DOM元素传递给它
  • 在v-draggable指令中,我们将拖拽数据、拖拽开始、拖拽过程、拖拽结束事件传递给了useDraggable hook
  • 在onDragStart事件中,我们不需要设置drag data,因为这一步在useDraggable hook中已经处理了
  • 在onDrag事件中,我们不需要手动设置
    元素的位置,因为useDraggable hook会自动更新位置
  • 在onDragEnd事件中,我们只需要还原
    的透明度即可

四、使用拖拽组件实现可视化拖拽编辑器

我们可以使用拖拽组件来实现一个可视化拖拽编辑器,让用户能够通过可视化的方式编辑页面布局。下面是一个使用Vue3和Element Plus实现的可视化拖拽编辑器的示例:

<template>
  <div class="drag-editor">
    <div class="drag-container">
      <draggable
        v-model="list"
        :animation="200"
        @end="onEnd">
        <div
          v-for="(item, index) in list"
          :key="item.id"
          :class="item.type"
          class="drag-item"
          ref="dragItem">
          {{ item.name }}
        </div>
      </draggable>
    </div>

    <el-dialog
      title="添加组件"
      :visible.sync="dialogVisible"
      width="30%"
      :before-close="beforeDialogClose">
      <el-select
        placeholder="请选择组件"
        v-model="component"
        style="width: 100%;">
        <el-option
          v-for="item in componentList"
          :key="item.id"
          :label="item.name"
          :value="item">
          <template #default>
            {{ item.name }}
          </template>
        </el-option>
      </el-select>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleAddComponent">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { ref } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import { ElDialog, ElSelect, ElOption, ElButton } from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';
import { useDraggable, useDialog } from '@vueuse/core';

export default {
  components: { ElDialog, ElSelect, ElOption, ElButton },

  setup() {
    const list = ref([
      {
        id: uuidv4(),
        type: 'header',
        name: 'Header',
      },
      {
        id: uuidv4(),
        type: 'paragraph',
        name: 'Paragraph',
      },
      {
        id: uuidv4(),
        type: 'image',
        name: 'Image',
      },
    ]);
    const component = ref(null);
    const dialogVisible = ref(false);
    const componentList = [
      { id: 1, name: 'Header' },
      { id: 2, name: 'Paragraph' },
      { id: 3, name: 'Image' },
    ];

    const { showDialog, hideDialog } = useDialog();

    const handleAddComponent = () => {
      if (component.value) {
        list.value.push({
          id: uuidv4(),
          type: component.value.type,
          name: component.value.name,
        });
        hideDialog();
      }
    };

    const { onDragStart, onDrop } = useDraggable({
      onDragStart: (event) => {
        event.dataTransfer.effectAllowed = 'move';
        const { target } = event;
        target.classList.add('dragging');
      },
    });

    const onEnd = (event) => {
      event.item.classList.remove('dragging');
    };

    return {
      list,
      component,
      dialogVisible,
      componentList,
      handleAddComponent,
      onDragStart,
      onDrop,
      onEnd,
      showDialog,
    };
  },
};
</script>

<style scoped>
.drag-editor {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100%;
}

.drag-container {
  display: flex;
  flex-wrap: wrap;
  max-width: 1024px;
  padding: 20px;
}

.drag-item {
  background-color: #1890ff;
  border-radius: 4px;
  color: #fff;
  cursor: move;
  display: inline-block;
  margin-right: 10px;
  margin-bottom: 10px;
  padding: 10px;
  user-select: none;
}

.dragging {
  opacity: 0.4;
}

.el-dialog__wrapper {
  display: none !important;
}
</style>

在这个示例中,我们使用了Element Plus的对话框、选择器等组件,并使用了vueuse库中的useDialog和useDraggable hook。具体来说:

  • 使用useDialog hook来封装对话框逻辑,表现为对话框的显示/隐藏、插槽分发等功能
  • 在handleAddComponent函数中,将选择的组件添加到list变量中,实现了向可视化编辑器中添加组件的功能
  • 在v-draggable指令中,我们仅传递onDragStart事件,因为useDraggable hook已经处理了其它拖拽事件
  • 在onEnd事件中,还原拖拽元素的透明度

五、结语

本文介绍了使用Vue3实现拖拽组件的基本用法和Composition API优化方法,并通过构建一个可视化拖拽编辑器的示例,展示了使用拖拽组件来改善用户体验的实际应用。通过学习Vue3拖拽组件的相关知识,我们可以快速构建交互式UI组件,并提高用户的工作效率。