index.vue 3.17 KB
<script lang="ts" setup name="SplitPane">
import { computed, ref } from 'vue'

const props = defineProps({
  direction: {
    type: String,
    default: 'row'
  },
  min: {
    type: Number,
    default: 10
  },
  max: {
    type: Number,
    default: 90
  },
  paneLengthPercent: {
    type: Number,
    default: 50
  },
  triggerLength: {
    type: Number,
    default: 10
  }
})

const emits = defineEmits(['setPaneLengthPercent']);

const triggerLeftOffset = ref(0) // 鼠标距滑动器左(顶)侧偏移量
const splitPane = ref();

const lengthType = computed(() => {
  return props.direction === 'row' ? 'width' : 'height'
})

const paneLengthValue = computed(() => {
  return `calc(${props.paneLengthPercent}% - ${props.triggerLength / 2 + 'px'})`
})

const triggerLengthValue = computed(() => {
  return props.triggerLength + 'px'
})

const paneRightValue = computed(() => {
  return `calc(${100 - props.paneLengthPercent}% - ${props.triggerLength / 2 + 'px'})`
})

// 按下滑动器
const handleMouseDown = (e) => {
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)

  if (props.direction === 'row') {
    triggerLeftOffset.value = e.pageX - e.srcElement.getBoundingClientRect().left
  } else {
    triggerLeftOffset.value = e.pageY - e.srcElement.getBoundingClientRect().top
  }
}

// 按下滑动器后移动鼠标
const handleMouseMove = (e) => {
  const clientRect = splitPane.value.getBoundingClientRect()
  let paneLengthPercent = 0

  if (props.direction === 'row') {
    const offset = e.pageX - clientRect.left - triggerLeftOffset.value + props.triggerLength / 2
    paneLengthPercent = (offset / clientRect.width) * 100
  } else {
    const offset = e.pageY - clientRect.top - triggerLeftOffset.value + props.triggerLength / 2
    paneLengthPercent = (offset / clientRect.height) * 100
  }

  if (paneLengthPercent < props.min) {
    paneLengthPercent = props.min
  }
  if (paneLengthPercent > props.max) {
    paneLengthPercent = props.max
  }

  emits('setPaneLengthPercent', paneLengthPercent)
}

// 松开滑动器
const handleMouseUp = () => {
  document.removeEventListener('mousemove', handleMouseMove)
}
</script>

<template>
  <div ref="splitPane" class="split-pane" :style="{'flex-direction': props.direction}">
    <div class="pane pane-one" :style="lengthType + ':' + paneLengthValue">
      <slot name="one"></slot>
    </div>
    <div class="pane-trigger" :style="lengthType + ':' + triggerLengthValue" @mousedown="handleMouseDown"></div>
    <div class="pane pane-two" :style="lengthType +':' + paneRightValue">
      <slot name="two"></slot>
    </div>
  </div>
</template>

<style scoped lang="scss">
.split-pane {
  background: #fafafa;
  height: 100%;
  display: flex;

  &.row {
    .pane {
      height: 100%;
    }

    .pane-trigger {
      height: 100%;
    }
  }

  &.column {
    .pane {
      width: 100%;
    }

    .pane-trigger {
      width: 100%;
    }
  }

  .pane-one {
    background: #fff;
  }

  .pane-trigger {
    user-select: none;
    background: #d9d9d9;
    cursor: col-resize;

    &:hover, &:focus {
      background: var(--el-color-primary);
    }
  }

  .pane-two {
    flex: 1;
    background: #fff;
  }
}</style>