vue自定義元件——split-pane

longxiaoming發表於2023-04-14

github地址: https://github.com/lxmghct/my-vue-components

元件介紹

  • props:
    • splitCount: 分割數量, default: 2
    • direction: 分割方向, 'vertical' or 'horizontal', default: 'horizontal'
    • defaultRatio: 預設比例, 型別為陣列, default: [1/spiltCount, 1/spiltCount, ...]
  • slots:
    • ...
  • events:
    • @resize: 拖動分割條時觸發, 引數為分割線兩側的div
    • @resize-stop: 拖動分割條結束時觸發
  • methods:
    • changeItemSize(index, itemSize, dire='next') 改變第item個pane的大小, dire為next或prev, 表示修改當前pane時連帶修改前一個pane還是後一個

效果展示

設計思路

整個元件採用flex佈局,透過設定整體的flex-direction控制分割方向,透過修改每個pane的style.flex控制每個pane的大小。

<div class="split-main" ref="splitMain"
     :class="direction === 'vertical' ? 'split-vertical' : 'split-horizontal'">
  <template v-if="direction === 'vertical'">
    <div v-for="i in splitCount" :key="i" ref="splitItem"
         class="split-vertical-item">
      <div class="split-vertical-line" v-if="i < splitCount"
           @mousedown="_startDrag(i)"
           @touchstart="_startDrag(i)"></div>
      <div class="split-vertical-content">
        <slot :name="`pane${i}`"></slot>
      </div>
    </div>
  </template>
  <template v-else>
    <div v-for="i in splitCount" :key="i" ref="splitItem"
         class="split-horizontal-item">
      <div class="split-horizontal-line" v-if="i < splitCount"
           @mousedown="_startDrag(i)"
           @touchstart="_startDrag(i)"></div>
      <div class="split-horizontal-content">
        <slot :name="`pane${i}`"></slot>
      </div>
    </div>
  </template>
</div>

透過v-for迴圈生成分割數量的pane,每個pane中間插入分割線,分割線透過@mousedown@touchstart事件繫結_startDrag方法,該方法用於監聽滑鼠或手指的移動事件,從而實現拖動分割線改變pane大小的功能。

_startDrag (index) {
  this.dragIndex = index - 1
},
_onMouseMove (e) {
  if (this.dragIndex === -1) {
    return
  }
  let items = this.$refs.splitItem
  let item1 = items[this.dragIndex]
  let item2 = items[this.dragIndex + 1]
  let rect1 = item1.getBoundingClientRect()
  let rect2 = item2.getBoundingClientRect()
  let ratio1, ratio2
  let minLen = this.minLen
  if (this.direction === 'vertical') {
    let height = this.$refs.splitMain.clientHeight
    let tempY = e.clientY - rect1.top > minLen ? e.clientY : rect1.top + minLen
    tempY = rect2.bottom - tempY > minLen ? tempY : rect2.bottom - minLen
    ratio1 = (tempY - rect1.top) / height
    ratio2 = (rect2.bottom - tempY) / height
  } else {
    let width = this.$refs.splitMain.clientWidth
    let tempX = e.clientX - rect1.left > minLen ? e.clientX : rect1.left + minLen
    tempX = rect2.right - tempX > minLen ? tempX : rect2.right - minLen
    ratio1 = (tempX - rect1.left) / width
    ratio2 = (rect2.right - tempX) / width
  }
  item1.style.flex = ratio1
  item2.style.flex = ratio2
  e.preventDefault()
  this.$emit('resize', item1, item2)
},
_onMouseUp () {
  if (this.dragIndex === -1) {
    return
  }
  this.dragIndex = -1
  this.$emit('resize-stop')
}

完整程式碼在github上。https://github.com/lxmghct/my-vue-components

相關文章