vue-draggable-resizable 可拖拽縮放的組件

Vue 用於可調整大小和可拖動元素的組件並支持組件之間的衝突檢測與組件對齊

前言

17年就應用此組件到了項目中,當時正式版的功能不能知足項目需求,還拉取了 dev分支的測試版進行了簡單的更改。(項目中主要功能之一須要用到此組件)
今年由於需求變動,項目重構(手動淚奔),而後去看了看 github,該組件的正式版本更新到了 1.7.x,因而把正式版拉下來根據本身的需求進行了修改併發布新版到npm上。

特徵

  • 沒有依賴
  • 可拖動,可調整大小或者二者都行
  • 擁有用於調整大小的控制點
  • 限制組件調整大小和移動超出父元素
  • 自定義網格移動
  • 將拖動限制爲垂直或水平移動

新增特徵php

  • 組件之間的衝突檢測(不容許組件之間重疊)
  • 組件與組件之間進行對齊(相似於吸附效果)

項目地址

原組件地址:vue-draggable-resizable
新組件地址:vue-draggable-resizablevue

安裝使用

npm install --save vue-draggable-resizable-gorkys

更多API請在項目說明文檔中查看jquery

Demo

Demogit

修改過程記錄

提出建議

github

在原組件的 Issues中提出了建議,做者表示不打算讓此組件進行跨越組件以外的操做。
好吧,既然做者不打算進行這方面的支持,那隻好本身動手了。

Clone項目

Fork項目到本身的倉庫,而後Clone項目到本地進行修改。github

需求說明

1.組件之間的衝突檢測npm

兩個組件不容許重疊,若是重疊,將回到移動或縮放前位置

2.組件與組件之間進行對齊(參照Jquery UI的draggable併發

用戶移動一個組件到另外一個組件邊緣的時候,進行對齊操做。相似於吸附效果

代碼修改

1.組件之間的衝突檢測

首先是組件之間的衝突檢測,組件與組件的邊界檢測須要一個標記進行判斷。測試

先在 props中加入一個 isConflictCheck,讓使用者本身選擇是否使用此功能。
props:{
    /* 定義組件是否開啓衝突檢測 */
    isConflictCheck: {
      type: Boolean, default: false
    }
    ...
}
當咱們拿到 isConflictCheck後,在 setConflictCheck方法中給組件的 Dom設置一個 data-*的屬性。
setConflictCheck: function () {
      if (this.isConflictCheck) {
        this.$el.setAttribute('data-is-check', 'true')
      } else {
        this.$el.setAttribute('data-is-check', 'false')
      }
    }
而後就是如何去檢測組件之間的衝突,代碼以下,此代碼是在測試版本中使用的,看到這些判斷均可怕,爲了頭髮,就沒有去優化了(反正能使用)。
conflictCheck: function () {
      if (this.isConflictCheck) {
        let p = this.$el.parentNode.childNodes // 獲取當前父節點下全部子節點
        if (p.length > 1) {
          for (let i = 0; i < p.length; i++) {
            if (p[i] !== this.$el && p[i].className !== undefined && p[i].getAttribute('data-is-check') !== 'false') {
              let tw = p[i].offsetWidth
              let th = p[i].offsetHeight
              let tl = p[i].offsetLeft
              let tt = p[i].offsetTop
              // 若是衝突,就將回退到移動前的位置
              if (this.top >= tt && this.left >= tl && tt + th > this.top && tl + tw > this.left ||
                this.top <= tt && this.left < tl && this.top + this.height > tt && this.left + this.width > tl) { /* 左上角與右下角重疊 */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              } else if (this.left <= tl && this.top >= tt && this.left + this.width > tl && this.top < tt + th ||
                this.top < tt && this.left > tl && this.top + this.height > tt && this.left < tl + tw) { /* 右上角與左下角重疊 */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              } else if (this.top < tt && this.left <= tl && this.top + this.height > tt && this.left + this.width > tl ||
                this.top > tt && this.left >= tl && this.top < tt + th && this.left < tl + tw) { /* 下邊與上邊重疊 */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              } else if (this.top <= tt && this.left >= tl && this.top + this.height > tt && this.left < tl + tw ||
                this.top >= tt && this.left <= tl && this.top < tt + th && this.left > tl + tw) { /* 上邊與下邊重疊(寬度不同) */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              } else if (this.left >= tl && this.top >= tt && this.left < tl + tw && this.top < tt + th ||
                this.top > tt && this.left <= tl && this.left + this.width > tl && this.top < tt + th) { /* 左邊與右邊重疊 */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              } else if (this.top <= tt && this.left >= tl && this.top + this.height > tt && this.left < tl + tw ||
                this.top >= tt && this.left <= tl && this.top < tt + th && this.left + this.width > tl) { /* 左邊與右邊重疊(高度不同) */
                this.top = this.restoreY
                this.left = this.restoreX
                this.width = this.restoreW
                this.height = this.restoreH
              }
            }
          }
        }
      }
    }, // 衝突檢測
最後就是在中止移動和縮放時調用上面的方法就能夠了(代碼精簡過)。
handleUp: function (e) {
      this.handle = null
      if (this.resizing) {
        this.resizing = false
        this.conflictCheck() // 衝突檢測
      }
      if (this.dragging) {
        this.dragging = false
        this.conflictCheck() // 衝突檢測
      }
    } // 鼠標鬆開

2.組件與組件之間進行對齊

與衝突檢測同樣的套路。優化

先在 props中加入一個 snap,讓使用者本身選擇是否使用此功能。爲了更靈活,這裏多添加了一個 snapTolerance,當調用對齊時,用來設置組件與組件之間的對齊距離,以像素爲單位。
/* 是否開啓元素對齊 */
    snap: {
      type: Boolean, default: false
    },
    /* 當調用對齊時,用來設置組件與組件之間的對齊距離,以像素爲單位。 */
    snapTolerance: {
      type: Number,
      default: 5,
      validator: function (val) {
        return typeof val === 'number'
      }
而後就是設置 data-*屬性
setSnap: function () {
      if (this.snap) {
        this.$el.setAttribute('data-is-snap', 'true')
      } else {
        this.$el.setAttribute('data-is-snap', 'false')
      }
    }, // 設置對齊元素
再而後就是主要方法 snapCheck的編寫。這裏我翻看了一下JQuery UI中的 draggable源碼,並近乎copy的借鑑了過來。
snapCheck: function () {
      if (this.snap) {
        let p = this.$el.parentNode.childNodes // 獲取當前父節點下全部子節點
        if (p.length > 1) {
          let x1 = this.left
          let x2 = this.left + this.width
          let y1 = this.top
          let y2 = this.top + this.height
          for (let i = 0; i < p.length; i++) {
            if (p[i] !== this.$el && p[i].className !== undefined && p[i].getAttribute('data-is-snap') !== 'false') {
              let l = p[i].offsetLeft // 對齊目標的left
              let r = l + p[i].offsetWidth // 對齊目標右側距離窗口的left
              let t = p[i].offsetTop// 對齊目標的top
              let b = t + p[i].offsetHeight // 對齊目標右側距離窗口的top

              let ts = Math.abs(t - y2) <= this.snapTolerance
              let bs = Math.abs(b - y1) <= this.snapTolerance
              let ls = Math.abs(l - x2) <= this.snapTolerance
              let rs = Math.abs(r - x1) <= this.snapTolerance
              if (ts) {
                this.top = t - this.height
              }
              if (bs) {
                this.top = b
              }
              if (ls) {
                this.left = l - this.width
              }
              if (rs) {
                this.left = r
              }
            }
          }
        }
      }
    }, // 檢測對齊元素
好了,最後就是在鼠標移動組件時及時調用就能夠了。
handleMove: function (e) {
        ...
        this.snapCheck()
        this.$emit('dragging', this.left, this.top)
      }
    }, // 鼠標移動

總結

此次的修改還算是很是順利,順便還把以前的一些代碼進行了優化。
若是發現什麼bug或者能夠將代碼優化的地方請勞煩告知我ui

相關文章
相關標籤/搜索