欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue鼠標(biāo)右鍵畫矩形和Ctrl按鍵多選組件方式

 更新時間:2024年12月26日 09:02:40   作者:-小龍人  
文章介紹了一個Vue組件,該組件允許用戶通過鼠標(biāo)右鍵在畫布上繪制矩形,并且支持通過Ctrl鍵進(jìn)行多選,文章附帶了組件代碼和一個示例,建議讀者將代碼復(fù)制到自己的開發(fā)環(huán)境中進(jìn)行調(diào)試

Vue鼠標(biāo)右鍵畫矩形和Ctrl按鍵多選組件

效果圖

說明

下面會貼出組件代碼以及一個Demo,上面的效果圖即為Demo的效果,建議直接將兩份代碼拷貝到自己的開發(fā)環(huán)境直接運行調(diào)試。

組件代碼

<template>
  <!-- 鼠標(biāo)畫矩形選擇對象 -->
  <div class="objects" ref="objectsRef" @mousedown="handleMouseDown">
    <!-- 矩形選擇框 -->
    <div
      class="mask"
      ref="maskRef"
      v-show="maskPosition.show"
      :style="
        'width:' +
        maskWidth +
        'left:' +
        maskLeft +
        'height:' +
        maskHeight +
        'top:' +
        maskTop
      "
    />

    <!-- 選擇對象內(nèi)容的目標(biāo)插槽 -->
    <slot name="selcetObject" />
  </div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, ref, computed } from "vue";

const props = withDefaults(
  defineProps<{
    objectClassName: string; // 選擇對象的class name,用于定義如何獲取對象
    objectIdName: string; // 選擇對象的id name,用于定義如何獲取對象的id
    selectObjectIds?: Array<string>; // 選中的對象ID
    selectObjects?: Array<HTMLElement>; // 選中的對象
    useCtrlSelect?: boolean; // 是否支持按住Ctrl多選
  }>(),
  {
    useCtrlSelect: true // 默認(rèn)支持按住Ctrl多選
  }
);

const objectsRef = ref();
const maskRef = ref();
const emits = defineEmits(["update:selectObjects", "update:selectObjectIds"]);
const state = reactive({
  maskPosition: {
    show: false,
    startX: 0,
    startY: 0,
    endX: 0,
    endY: 0
  }, // 矩形框位置
  isPressCtrlKey: false // 是否按下了Ctrl鍵
});
const { maskPosition, isPressCtrlKey } = toRefs(state);

// 若支持按住Ctrl多選,監(jiān)聽Ctrl事件
if (props.useCtrlSelect) {
  // 釋放
  document.addEventListener("keyup", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = false;
    }
  });
  // 按下
  document.addEventListener("keydown", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = true;
    }
  });
}

/** 鼠標(biāo)按下 */
const handleMouseDown = event => {
  // 展示矩形框,通過坐標(biāo)位置來畫出矩形
  maskPosition.value.show = true;
  maskPosition.value.startX = event.clientX;
  maskPosition.value.startY = event.clientY;
  maskPosition.value.endX = event.clientX;
  maskPosition.value.endY = event.clientY;
  // 監(jiān)聽鼠標(biāo)移動事件和抬起離開事件
  objectsRef.value.addEventListener("mousemove", handleMouseMove);
  objectsRef.value.addEventListener("mouseup", handleMouseUp);
};

/** 鼠標(biāo)移動 */
const handleMouseMove = event => {
  maskPosition.value.endX = event.clientX;
  maskPosition.value.endY = event.clientY;
};

/** 鼠標(biāo)抬起離開 */
const handleMouseUp = () => {
  // 移除鼠標(biāo)監(jiān)聽事件
  objectsRef.value.removeEventListener("mousemove", handleMouseMove);
  objectsRef.value.removeEventListener("mouseup", handleMouseUp);
  maskPosition.value.show = false;
  handleResetMaskPosition();
  handleGetSelectObject();
};

/** 獲取選擇的對象 */
const handleGetSelectObject = () => {
  // 選中對象ID和對象元素
  let tempSelectObjectIds: Array<string> = [];
  let tempSelectObjects: Array<HTMLElement> = [];

  // 如果按下了Ctrl鍵,之前選擇的數(shù)據(jù)不清空
  if (isPressCtrlKey.value) {
    tempSelectObjectIds =
      props.selectObjectIds === undefined ? [] : props.selectObjectIds;
    tempSelectObjects =
      props.selectObjects === undefined ? [] : props.selectObjects;
  }

  // 獲取鼠標(biāo)畫出的矩形框位置
  const rectanglePosition = maskRef.value.getClientRects()[0];

  // 獲取所有選擇區(qū)域的對象; 這里獲取的元素的方式定義于父組件的objectClassName
  const selectedObjects = objectsRef.value.querySelectorAll(
    `.${props.objectClassName}`
  );
  // 遍歷對象,獲取到每個對象的坐標(biāo)位置,判斷該位置是否在上面獲取到的鼠標(biāo)畫矩形的框的位置中
  selectedObjects.forEach(item => {
    const objectPosition = item.getClientRects()[0];

    // 這里獲取的id的方式定義于父組件的objectIdName
    if (compareObjectPosition(objectPosition, rectanglePosition)) {
      const id = item.getAttribute(props.objectIdName);

      // 如果按下了Ctrl鍵
      if (isPressCtrlKey.value) {
        // 已被選中的需要被取消選中
        if (tempSelectObjectIds.includes(id)) {
          tempSelectObjectIds = tempSelectObjectIds.filter(a => a != id);
          tempSelectObjects = tempSelectObjects.filter(a => a != item);
        } else {
          tempSelectObjectIds.push(id);
          tempSelectObjects.push(item);
        }
      } else {
        tempSelectObjectIds.push(id);
        tempSelectObjects.push(item);
      }
    }
  });

  // 回傳到父組件
  emits("update:selectObjects", tempSelectObjects);
  emits("update:selectObjectIds", tempSelectObjectIds);
};

/**
 * 判斷對象坐標(biāo)是否在鼠標(biāo)畫出的矩形框坐標(biāo)位置內(nèi)
 * @param objectPosition 對象坐標(biāo)位置
 * @param rectanglePosition 鼠標(biāo)畫出的矩形框坐標(biāo)位置
 */
const compareObjectPosition = (objectPosition, rectanglePosition) => {
  const maxX = Math.max(
    objectPosition.x + objectPosition.width,
    rectanglePosition.x + rectanglePosition.width
  );
  const maxY = Math.max(
    objectPosition.y + objectPosition.height,
    rectanglePosition.y + rectanglePosition.height
  );
  const minX = Math.min(objectPosition.x, rectanglePosition.x);
  const minY = Math.min(objectPosition.y, rectanglePosition.y);
  return (
    maxX - minX <= objectPosition.width + rectanglePosition.width &&
    maxY - minY <= objectPosition.height + rectanglePosition.height
  );
};

/** 重置鼠標(biāo)位置 */
const handleResetMaskPosition = () => {
  maskPosition.value.startX = 0;
  maskPosition.value.startY = 0;
  maskPosition.value.endX = 0;
  maskPosition.value.endY = 0;
};

/** 通過鼠標(biāo)位置實時計算矩形框大小 */
const maskWidth = computed(() => {
  return `${Math.abs(maskPosition.value.endX - maskPosition.value.startX)}px;`;
});
const maskHeight = computed(() => {
  return `${Math.abs(maskPosition.value.endY - maskPosition.value.startY)}px;`;
});
const maskLeft = computed(() => {
  return `${Math.min(maskPosition.value.startX, maskPosition.value.endX)}px;`;
});
const maskTop = computed(() => {
  return `${Math.min(maskPosition.value.startY, maskPosition.value.endY)}px;`;
});
</script>
<style scoped lang="scss">
.objects {
  height: 100%;
  width: 100%;
  overflow-y: auto;

  .mask {
    position: fixed;
    background: #409eff;
    opacity: 0.4;
    z-index: 100;
  }
}
</style>

Demo

建議直接將上面組件命名為 MouseDrawRectangle

<template>
  <!------------- 鼠標(biāo)畫矩形選擇對象組件DEMO,可以直接拷貝到你的頁面去運行----------------------->
  <div class="content">
    <!-- 
    MouseDrawRectangle說明:
    objectClassName綁定到下面對象class名稱; 
    objectIdName名稱對應(yīng)object_id;
    useCtrlSelect默認(rèn)是打開的,用于按住Ctrl鍵進(jìn)行多選,以及取消已選擇的對象。
    
    selectObjectIds會實時從子組件更新過來,監(jiān)聽它的值來控制頁面的選擇狀態(tài)即可。
    另外有參數(shù)selectObjects會實時從子組件傳回被選中的對象Dom信息
    -->
    <MouseDrawRectangle
      objectClassName="select_object"
      objectIdName="object_id"
      :useCtrlSelect="true"
      v-model:selectObjectIds="selectObjectIds"
      v-model:selectObjects="selectObjects"
    >
      <!-- 這個是插槽,將業(yè)務(wù)內(nèi)容的Dom限制在MouseDrawRectangle組件內(nèi),
      這樣可以將后面組件所有的監(jiān)聽事件綁定到組件上而不是整個頁面Dom上,
      鼠標(biāo)滑動的區(qū)域也會限制死在組件內(nèi),而不是整個頁面的范圍 -->
      <template #selcetObject>
        <div class="objects_content">
          <!-- 每一個選擇的目標(biāo)對象 -->
          <div
            v-for="item in 50"
            :key="item"
            class="select_object"
            :object_id="item"
            :class="
              selectObjectIds.includes(item.toString()) ? 'is_selected' : ''
            "
          >
            {{ item }}
          </div>
        </div>
      </template>
    </MouseDrawRectangle>
  </div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, watch } from "vue";
import MouseDrawRectangle from "@/components/objectSelect/mouseDrawRectangle.vue";

const state = reactive({
  selectObjectIds: [] as Array<string>, // 選中的對象ID
  selectObjects: [] as Array<HTMLElement> // 選中的對象DOM
});
const { selectObjectIds, selectObjects } = toRefs(state);

watch(
  () => [selectObjectIds.value, selectObjects.value],
  () => {
    console.log("選中的ID=>", selectObjectIds);
    console.log("選中的Dom=>", selectObjects);
  }
);
</script>
<style scoped lang="scss">
.content {
  // 因為使用flex布局,最下面一行盒子換行只會出現(xiàn)一半的高度,這里最好減去下每個盒子的高度
  height: calc(100% - 50px);
  overflow-y: auto;
  padding: 20px;

  .objects_content {
    user-select: none;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 10px;

    // 盒子樣式
    > div {
      width: 200px;
      height: 100px;
      background-color: #999;
    }

    .is_selected {
      color: #fff;
      box-sizing: border-box;
      border: 3px #317aff solid;
      border-radius: 5px;
    }
  }
}
</style>

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue實現(xiàn)一個返回頂部backToTop組件

    Vue實現(xiàn)一個返回頂部backToTop組件

    本篇文章主要介紹了Vue實現(xiàn)一個返回頂部backToTop組件,可以實現(xiàn)回到頂部效果,具有一定的參考價值,有興趣的可以了解一下
    2017-07-07
  • vue mintui-Loadmore結(jié)合實現(xiàn)下拉刷新和上拉加載示例

    vue mintui-Loadmore結(jié)合實現(xiàn)下拉刷新和上拉加載示例

    本篇文章主要介紹了vue mintui-Loadmore結(jié)合實現(xiàn)下拉刷新和上拉加載示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • vue-cli+webpack項目 修改項目名稱的方法

    vue-cli+webpack項目 修改項目名稱的方法

    下面小編就為大家分享一篇vue-cli+webpack項目 修改項目名稱的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • vite打包靜態(tài)文件打開顯示空白的解決

    vite打包靜態(tài)文件打開顯示空白的解決

    本文主要介紹了vite打包靜態(tài)文件打開顯示空白的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • 基于vue-cli、elementUI的Vue超簡單入門小例子(推薦)

    基于vue-cli、elementUI的Vue超簡單入門小例子(推薦)

    這篇文章主要介紹了基于vue-cli、elementUI的Vue超簡單入門小例子,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vant實現(xiàn)購物車功能

    vant實現(xiàn)購物車功能

    這篇文章主要為大家詳細(xì)介紹了vant實現(xiàn)購物車功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • 詳解vue中使用vue-quill-editor富文本小結(jié)(圖片上傳)

    詳解vue中使用vue-quill-editor富文本小結(jié)(圖片上傳)

    這篇文章主要介紹了詳解vue中使用vue-quill-editor富文本小結(jié)(圖片上傳),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vue路由篇之router-view內(nèi)容無法渲染出來問題

    vue路由篇之router-view內(nèi)容無法渲染出來問題

    這篇文章主要介紹了vue路由篇之router-view內(nèi)容無法渲染出來問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue之全局水印的實現(xiàn)示例

    Vue之全局水印的實現(xiàn)示例

    頁面水印大家或許都不陌生,本文主要介紹了Vue之全局水印的實現(xiàn)示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 用Vue.js在瀏覽器中實現(xiàn)裁剪圖像功能

    用Vue.js在瀏覽器中實現(xiàn)裁剪圖像功能

    在本教程中,我們將探討如何在瀏覽器中使用 JavaScript 庫來操作圖片,為服務(wù)器上的存儲做準(zhǔn)備,并在 Web 程序中使用。我們將使用 Vue.js 而不是原生 JavaScript來完成此操作,需要的朋友可以參考下
    2019-06-06

最新評論