首先,我们需要定义一个 Selection 类型,来表示当前选择区域的位置和大小:

interface Selection {
  startX: number;
  startY: number;
  endX: number;
  endY: number;
}

然后,我们可以创建一个 SelectionBox 组件,来实现鼠标框选的功能:

<template>
  <div class="selection-box" @mousedown="onMouseDown" @mousemove="onMouseMove" @mouseup="onMouseUp">
    <div class="overlay" v-if="selection">
      <div :style="selectionStyle"></div>
    </div>
    <slot></slot>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watchEffect } from 'vue';

interface Selection {
  startX: number;
  startY: number;
  endX: number;
  endY: number;
}

export default defineComponent({
  name: 'SelectionBox',
  setup() {
    const startX = ref(0);
    const startY = ref(0);
    const endX = ref(0);
    const endY = ref(0);
    const selection = ref<Selection | null>(null);

    const onMouseDown = (event: MouseEvent) => {
      startX.value = event.clientX;
      startY.value = event.clientY;
      endX.value = event.clientX;
      endY.value = event.clientY;
      selection.value = {
        startX: startX.value,
        startY: startY.value,
        endX: endX.value,
        endY: endY.value,
      };
    };

    const onMouseMove = (event: MouseEvent) => {
      if (selection.value) {
        endX.value = event.clientX;
        endY.value = event.clientY;
        selection.value = {
          startX: startX.value,
          startY: startY.value,
          endX: endX.value,
          endY: endY.value,
        };
      }
    };

    const onMouseUp = () => {
      selection.value = null;
    };

    const selectionStyle = ref({
      left: `${Math.min(startX.value, endX.value)}px`,
      top: `${Math.min(startY.value, endY.value)}px`,
      width: `${Math.abs(endX.value - startX.value)}px`,
      height: `${Math.abs(endY.value - startY.value)}px`,
    });

    watchEffect(() => {
      if (selection.value) {
        selectionStyle.value = {
          left: `${Math.min(startX.value, endX.value)}px`,
          top: `${Math.min(startY.value, endY.value)}px`,
          width: `${Math.abs(endX.value - startX.value)}px`,
          height: `${Math.abs(endY.value - startY.value)}px`,
        };
      }
    });

    return { onMouseDown, onMouseMove, onMouseUp, selectionStyle, selection };
  },
});
</script>

<style scoped>
.selection-box {
  position: relative;
}

.overlay {
  position: absolute;
  z-index: 1;
  border: 2px dashed #000;
  opacity: 0.5;
}
</style>

SelectionBox 组件中,我们监听了 mousedownmousemovemouseup 事件,实现了鼠标框选的功能。同时,我们也定义了一个 selectionStyle 变量,用于动态设置选择区域的样式。

最后,我们可以在应用中使用 SelectionBox 组件:

<template>
  <div>
    <SelectionBox>
      <div v-for="item in items" :key="item.id" :style="itemStyle(item)">
        {{ item.text }}
      </div>
    </SelectionBox>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import SelectionBox from './SelectionBox.vue';

interface Item {
  id: number;
  text: string;
  x: number;
  y: number;
}

export default defineComponent({
  name: 'App',
  components: { SelectionBox },
  setup() {
    const items = [
      { id: 1, text: 'Item 1', x: 100, y: 100 },
      { id: 2, text: 'Item 2', x: 200, y: 200 },
      { id: 3, text: 'Item 3', x: 300, y: 300 },
      { id: 4, text: 'Item 4', x: 400, y: 400 },
    ];

    const itemStyle = (item: Item) => ({
      position: 'absolute',
      left: `${item.x}px`,
      top: `${item.y}px`,
      border: '1px solid #000',
      padding: '10px',
    });

    return { items, itemStyle };
  },
});
</script>

在这个例子中,我们将 SelectionBox 组件包裹在一些元素的外面,然后通过 v-for 渲染了一些带有位置信息的元素。当用户使用鼠标框选时,我们可以通过计算选择区域和元素的位置信息,来确定哪些元素被选中了

vue3 Typescript 实现鼠标框选

原文地址: https://www.cveoy.top/t/topic/exbR 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录