欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

【WEB開發】輕松搞定自定義對話框窗口拖拽縮放、移動

admin
2025年3月31日 18:17 本文熱度 114

經常會碰到需要拖拽縮放的情況,只要有思路,實現起來會非常順暢。
功能的核心是鼠標放在四個邊和角上,拖拽把容器放大或縮小

功能演示

縮放:

?

移動:

演示網址:寶藏導航?


縮放設計思路

  1. 使用css繪制四條邊和四個角
  2. 通過css定位,控制四根線和四個角在對應的位置
  3. 監聽鼠標點擊和移動事件
  4. 在移動的過程中,改變容器的大小

核心設計

基礎html結構

<template>
  <!-- 使用 v-if 判斷是否插入到 body 中 -->
  <!-- 創建一個容器,支持拖拽,使用 ref 引用該容器 -->
  <div
    ref="draggableContainer"
    class="draggable-container"
    @mousedown="startDrag"
    :style="containerStyle"
  >
    <!-- 插槽,用戶可以將其他內容插入到這個容器中 -->
    <slot></slot>

    <!-- 創建縮放控制點,每個控制點代表一個邊角,使用 v-for 循環渲染 -->
    <span
      v-for="type in resizeTypes"
      :key="type"
      :class="`${type}-resize`"
      @mousedown="startResize($event, type)"
    ></span>
  </div>
</template>

基礎data數據:

data: {
      // 定義可縮放的邊和角的類型
      resizeTypes: ["lt", "t", "rt", "r", "rb", "b", "lb", "l"],
      // 定義容器位置和大小的響應式數據
      position: { x: this.left, y: this.top }, // 容器的位置
      size: { width: this.width, height: this.height }, // 容器的尺寸
  }

核心代碼和思路

容器新寬度 = 容器初始寬度 + 鼠標移動距離

通過上面公式,我們需要記錄

  1. 容器的初始寬度
  2. 鼠標移動距離 = 鼠標新位置 - 鼠標初始距離

1. 記錄容器和鼠標初始狀態

// 鼠標按下,開始拖拽
startResize(event) {
    // 記錄鼠標初始位置
    this.originMouseX = event.clientX;
    this.originMouseY = event.clientY;

    // 記錄容器初始寬高
    this.originWidth = this.size.width;
    this.originHeight = this.size.height;
},

2. 計算拖拽后新寬度

根據:容器新寬度 = 容器初始寬度 + 鼠標移動距離

拖拽容器右邊:

// 計算鼠標的移動距離 deltaX
const deltaX = event.clientX - this.originMouseX; 
// 容器新寬度 = 初始寬度 + 鼠標移動距離
newWidth = this.originWidth + deltaX; 

拖拽容器右下角:
當我們拖拽容器右下角,容器的寬和高都會改變。我們需要把這個拆分成兩個步驟來解決。

// 獲取新寬度
const deltaX = event.clientX - this.originMouseX; 
newWidth = this.originWidth + deltaX;

// 獲取新高度
const deltaY = event.clientY - this.originMouseY; // 計算鼠標的縱向位移
newHeight = this.originHeight + deltaY;

拖拽左邊和上邊:
拖拽左邊的時候,左邊的定位不能始終都是在原來的位置。
假設:
    我們的初始位置是 left: 200px。左邊向左拖拽50px后,需要變為left: 150px 首先我們需要在開始的時候記錄容器的初始位置

// 鼠標按下,開始拖拽
startResize(event) {
    // 記錄鼠標初始位置
    this.originMouseX = event.clientX;
    this.originMouseY = event.clientY;

    // 記錄容器初始寬高
    this.originWidth = this.size.width;
    this.originHeight = this.size.height;
    
    // 記錄容器初始位置
    this.originContainX = this.position.x;
    this.originContainY = this.position.y;
}

改變寬高的同時,改變容器左上角的位置

// 改變高度
const deltaX = event.clientX - this.originMouseX; 
newWidth = this.originWidth - deltaX;

// 改變左邊的位置
this.position.x = this.originContainX + deltaX;

3. 確定拖拽的是哪條邊

我們在點擊的時候會傳遞type,使用變量把type記錄下。

<span
  v-for="type in resizeTypes"
  :key="type"
  :class="`${type}-resize`"
  @mousedown="startResize($event, type)"
></span>
// 鼠標按下,開始拖拽
startResize(event, type) {
  this.resizeType = type; // 記錄拖拽的邊角的類型
  ......
}

// 開始拖拽的過程中,改變容器狀態
handleResize() {
const deltaX = event.clientX - this.originMouseX; // 計算鼠標的橫向位移
const deltaY = event.clientY - this.originMouseY; // 計算鼠標的縱向位移

let newWidth = this.originWidth;
let newHeight = this.originHeight;

// 根據縮放類型計算新的容器尺寸
switch (this.resizeType) {
    case "lt": // 左上角
      newWidth = this.originWidth - deltaX;
      this.size.width = newWidth;
      this.position.x = this.originContainX + deltaX;
      
      newHeight = this.originHeight - deltaY;
      this.size.height = newHeight;
      this.position.y = this.originContainY + deltaY;
      break;
      
    case "t": // 上邊
      newHeight = this.originHeight - deltaY;
      this.size.height = newHeight;
      this.position.y = this.originContainY + deltaY;
      break;

      右邊,右下角同理......
}

4.設置最小的拖拽寬和高

如果新拖拽的寬度,已經小于最小寬度。拖拽時不進行任何改動。

switch (this.resizeType) {
    case "lt": // 左上角
      newWidth = this.originWidth - deltaX;
      newHeight = this.originHeight - deltaY;
      if (newWidth >= this.minWidth) {
        this.position.x = this.originContainX + deltaX;
        this.size.width = newWidth;
      }
      if (newHeight >= this.minHeight) {
        this.position.y = this.originContainY + deltaY;
        this.size.height = newHeight;
      }
      break;
    case "t": // 上邊
      newHeight = this.originHeight - deltaY;
      if (newHeight >= this.minHeight) {
        this.position.y = this.originContainY + deltaY;
        this.size.height = newHeight;
      }
      break;
      
      右邊邊,右下角同理......
}

5.添加拖拽移動

拖拽移動的詳細內容,筆者寫的另一篇文章:拖拽移動詳細思路
下面的完整代碼是結合了拖拽移動和縮放整合在一起,一個較為完整的拖拽組件


完整代碼

<template>
  <!-- 使用 v-if 判斷是否插入到 body 中 -->
  <!-- 創建一個容器,支持拖拽,使用 ref 引用該容器 -->
  <div
    ref="draggableContainer"
    class="draggable-container"
    @mousedown="startDrag"
    :style="containerStyle"
  >
    <!-- 插槽,用戶可以將其他內容插入到這個容器中 -->
    <slot></slot>

    <!-- 創建縮放控制點,每個控制點代表一個邊角,使用 v-for 循環渲染 -->
    <span
      v-for="type in resizeTypes"
      :key="type"
      :class="`${type}-resize`"
      @mousedown="startResize($event, type)"
    ></span>
  </div>
</template>

<script>
export default {
  props: {
    zIndex: { type: Number, default: 1 }, // 層級,控制顯示順序
    left: { type: Number, default: 0 }, // 容器的初始 X 位置
    top: { type: Number, default: 0 }, // 容器的初始 Y 位置
    width: { type: Number, default: 300 }, // 容器的初始寬度
    height: { type: Number, default: 300 }, // 容器的初始高度
    minWidth: { type: Number, default: 100 }, // 容器的最小寬度
    minHeight: { type: Number, default: 100 }, // 容器的最小高度
  },
  data() {
    return {
      // 定義可縮放的邊和角的類型
      resizeTypes: ["lt", "t", "rt", "r", "rb", "b", "lb", "l"],
      // 定義容器位置和大小的響應式數據
      position: { x: this.left, y: this.top }, // 容器的位置
      size: { width: this.width, height: this.height }, // 容器的尺寸
      originMouseX: 0, // 鼠標初始 X 坐標
      originMouseY: 0, // 鼠標初始 Y 坐標
      originContainX: 0, // 容器初始 X 坐標
      originContainY: 0, // 容器初始 Y 坐標
      originWidth: 0, // 容器初始寬度
      originHeight: 0, // 容器初始高度
      resizeType: "", // 當前縮放類型
    };
  },
  computed: {
    // 計算容器的樣式
    containerStyle() {
      return {
        top: `${this.position.y}px`, // 設置容器的 top 樣式
        left: `${this.position.x}px`, // 設置容器的 left 樣式
        width: `${this.size.width}px`, // 設置容器的寬度
        height: `${this.size.height}px`, // 設置容器的高度
        zIndex: this.zIndex, // 設置容器的層級
      };
    },
  },
  methods: {
    /**
     * 拖拽邏輯
     */
    startDrag(event) {
      // 記錄鼠標初始位置
      this.originMouseX = event.clientX;
      this.originMouseY = event.clientY;

      // 記錄容器初始位置
      this.originContainX = this.position.x;
      this.originContainY = this.position.y;

      // 添加鼠標移動和鼠標松開事件監聽
      document.addEventListener("mousemove", this.handleDrag);
      document.addEventListener("mouseup", this.stopDrag);
    },

    handleDrag(event) {
      this.position.x = this.originContainX + event.clientX - this.originMouseX;
      this.position.y = this.originContainY + event.clientY - this.originMouseY;
    },

    /**
     * 縮放邏輯
     */
    startResize(event, type) {
      this.resizeType = type; // 記錄拖拽的邊角的類型

      // 記錄鼠標初始位置
      this.originMouseX = event.clientX;
      this.originMouseY = event.clientY;

      // 記錄容器初始寬高
      this.originWidth = this.size.width;
      this.originHeight = this.size.height;

      // 記錄容器初始位置
      this.originContainX = this.position.x;
      this.originContainY = this.position.y;
      

      event.stopPropagation(); // 阻止事件傳播,防止觸發拖拽

      // 添加鼠標移動和鼠標松開事件監聽
      document.addEventListener("mousemove", this.handleResize);
      document.addEventListener("mouseup", this.stopDrag);
    },

    handleResize(event) {
      const deltaX = event.clientX - this.originMouseX; // 計算鼠標的橫向位移
      const deltaY = event.clientY - this.originMouseY; // 計算鼠標的縱向位移

      let newWidth = this.originWidth;
      let newHeight = this.originHeight;

      // 根據縮放類型計算新的容器尺寸
      switch (this.resizeType) {
        case "lt": // 左上角
          newWidth = this.originWidth - deltaX;
          newHeight = this.originHeight - deltaY;
          if (newWidth >= this.minWidth) {
            this.position.x = this.originContainX + deltaX;
            this.size.width = newWidth;
          }
          if (newHeight >= this.minHeight) {
            this.position.y = this.originContainY + deltaY;
            this.size.height = newHeight;
          }
          break;
        case "t": // 上邊
          newHeight = this.originHeight - deltaY;
          if (newHeight >= this.minHeight) {
            this.position.y = this.originContainY + deltaY;
            this.size.height = newHeight;
          }
          break;
        case "rt": // 右上角
          newWidth = this.originWidth + deltaX;
          newHeight = this.originHeight - deltaY;
          if (newWidth >= this.minWidth) {
            this.size.width = newWidth;
          }
          if (newHeight >= this.minHeight) {
            this.position.y = this.originContainY + deltaY;
            this.size.height = newHeight;
          }
          break;
        case "r": // 右邊
          newWidth = this.originWidth + deltaX;
          if (newWidth >= this.minWidth) {
            this.size.width = newWidth;
          }
          break;
        case "rb": // 右下角
          newWidth = this.originWidth + deltaX;
          newHeight = this.originHeight + deltaY;
          if (newWidth >= this.minWidth) {
            this.size.width = newWidth;
          }
          if (newHeight >= this.minHeight) {
            this.size.height = newHeight;
          }
          break;
        case "b": // 下邊
          newHeight = this.originHeight + deltaY;
          if (newHeight >= this.minHeight) {
            this.size.height = newHeight;
          }
          break;
        case "lb": // 左下角
          newWidth = this.originWidth - deltaX;
          newHeight = this.originHeight + deltaY;
          if (newWidth >= this.minWidth) {
            this.position.x = this.originContainX + deltaX;
            this.size.width = newWidth;
          }
          if (newHeight >= this.minHeight) {
            this.size.height = newHeight;
          }
          break;
        case "l": // 左邊
          newWidth = this.originWidth - deltaX;
          if (newWidth >= this.minWidth) {
            this.position.x = this.originContainX + deltaX;
            this.size.width = newWidth;
          }
          break;
      }
    },

    /**
     * 停止拖拽或縮放
     * 清除事件監聽器
     */
    stopDrag() {
      document.removeEventListener("mousemove", this.handleDrag);
      document.removeEventListener("mousemove", this.handleResize);
      document.removeEventListener("mouseup", this.stopDrag);
    },
  },

  // 組件銷毀時移除事件監聽
  beforeDestroy() {
    this.stopDrag();
  },
};
</script>
<style lang="scss" scoped>
$lineOffset: -6px;
$cornerOffset: -8px;
/* 拖拽容器的樣式 */
.draggable-container {
  position: fixed; /* 絕對定位 */
  cursor: move; /* 鼠標移動時顯示抓手指針 */
  user-select: none; /* 禁止選中文本 */
  background-color: #ccc;
  span {
    position: absolute;
    display: block;
  }
  /* 左邊和右邊 */
  .l-resize,
  .r-resize {
    width: 8px;
    height: 100%;
    top: 0;
    cursor: w-resize;
  }
  .l-resize {
    left: $lineOffset;
  }
  .r-resize {
    right: $lineOffset;
  }

  /* 上邊和下邊 */
  .t-resize,
  .b-resize {
    width: 100%;
    height: 8px;
    left: 0;
    cursor: s-resize;
  }
  .t-resize {
    top: $lineOffset;
  }
  .b-resize {
    bottom: $lineOffset;
  }
  /* 四個角 */
  .lt-resize,
  .rt-resize,
  .rb-resize,
  .lb-resize {
    width: 15px;
    height: 15px;
    z-index: 10;
  }
  .lt-resize,
  .lb-resize {
    left: $cornerOffset;
  }
  .lt-resize,
  .rt-resize {
    top: $cornerOffset;
  }
  .rt-resize,
  .rb-resize {
    right: $cornerOffset;
  }
  .rb-resize,
  .lb-resize {
    bottom: $cornerOffset;
  }

  .lt-resize,
  .rb-resize {
    cursor: se-resize;
  }
  .rt-resize,
  .lb-resize {
    cursor: sw-resize;
  }
}
</style>

組件引用

<DraggableContainer
  :width="400"
  :height="400"
  :min-height="300"
  :min-width="300"
>
  <div>能拖動我了</div>
</DraggableContainer>

總結

部分代碼設計參考了著名第三方庫vxe-modal的設計思路:vxe-modal

本文實現了拖拽移動和縮放的功能,同學們也可以根據需要往上面添加自己的改動。希望對您有所幫助!


作者:前端金熊
鏈接:https://juejin.cn/post/7451927213435093033
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

該文章在 2025/4/1 13:03:47 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved