Я пробовал много способов создать заголовок столбца перетаскивания, это может быть сложно, например, когда родительский контейнер имеет переполнение-x. Это лучший способ избежать вычислений и всего остального.

Шаблон Vue

Создайте ‹button› после каждого ‹li› или ‹th› по своему усмотрению и добавьте ссылку для каждой ‹button› и каждой ‹li› и поместите индекс для функции v-for.

<ul
  @mouseup="mouseUp($event)"
  @mousemove="mouseMove($event)">
  <li
    v-for="(header, index) in headers"
    :key="colIndex"
    :ref="`element-${index}`"
  >
    <button
      @mousedown="mouseDown($event, colIndex)"
      @mouseup="mouseUp($event)"
      :ref="`resize-${index}`"
      class="resize"
      :class="{ 'dragged': beforeChangeSize.col === index }"
     >
     </button>
  </li>
</ul>

Стиль

Кнопка должна появляться, когда вы наводите на нее курсор. При перетаскивании он занимает фиксированное положение.

li {
  position: relative;
}
.resize {
  position: absolute;
  top: 0;
  right: 0;
  height: 45px;
  background: #7d8ba5;
  width: 5px;
  padding: 0;
  border: 0;
  cursor: col-resize;
  box-shadow: none;
  outline: none;
  opacity: 0;
  transition: opacity ease .5s;
  &.dragged {
    opacity: 1;
    position: fixed;
    top: auto;
  }
  &:hover {
    opacity: 1;
  }
}

JavaScript

Создайте глобальную переменную с именем eventDrag, которая принимает значение true при dragStart и становится false при dragEnd. Эта переменная позволяет для включения или отключения содержимого функции moveDrag, moveEnd.

handleDownChangeSize(event, colIndex) {
  this.eventDrag = true
}
handleMoveChangeSize(event) {
  if (this.eventDrag) { ... }
}
handleUpDragToFill(event) {
  if (this.eventDrag) { ... }
},

MouseDown

Создайте объект под названием beforeChangeSize, когда dragStart содержит:

  • смещение элемента event.clientX.
  • индекс элемента colIndex
  • ширина элемента, element.innerWidth.
mouseDown(event, colIndex) {
  this.eventDrag = true;
  const [element] = this.$refs[`element-${this.beforeChangeSize.col}`]
  this.beforeChangeSize = {
    offset: event.clientX,
    col: colIndex,
    width: element.offsetWidth
  };
}

Примените offsetTop parentElement элемента к resizeButton.style.top

const [element] = this.$refs[`element-${this.beforeChangeSize.col}`]
const [resizeButton] = this.$refs[`resize-${colIndex}`]
resizeButton.style.top = `${element.parentElement.offsetTop}px`

MouseMove

Измените style.left элемента при перетаскивании. Элемент будет иметь перетаскиваемый класс и фиксированное положение.

mouseMove(event) {
  if (this.eventDrag) {
    const [resizeButton] = this.$refs[`resize-${this.beforeChangeSize.col}`]
    resizeButton.style.left = `${event.clientX}px`
  }
}

Если вы переместите курсор за пределы поля, вызывается функция mouseUp.

  • Получите offsetTop для resizeButton
  • Получить offsetHeight resizeButton
  • Добавить offsetTop в offsetHeight

Если offsetTop выше или равен event.clientY, а offsetBottom меньше или равен в event.clientY. Курсор находится в поле.

В противном случае вы находитесь вне рамок, поэтому вызывается функция mouseUp.

const [resizeButton] = this.$refs[`resize-${this.beforeChangeSize.col}`]
const offsetTop = resizeButton.offsetTop
const offsetBottom = offsetTop + resizeButton.offsetHeight
if (offsetTop <= event.clientY && offsetBottom >=  event.clientY) {
  // Change style.left of resizeButton
} else {
  // Called MouseUp function
}

MouseUp

Чтобы рассчитать новый размер элемента, вычтите oldOffset из newOffset и обязательно вычтите максимум из минимума, для этого используйте Math.max , Math.min.

const oldOffset = this.beforeChangeSize.offset
const newOffset = event.clientX
const result = Math.max(newOffset, oldOffset) - Math.min(newOffset, oldOffset)

Как только результат готов, вопрос состоит в том, чтобы добавить или вычесть результат начальной ширины.

  • Если newOffset превосходит oldOffset, добавьте результат
  • Иначе вычесть результат
if (newOffset > oldOffset) {
  this.newSize = this.beforeChangeSize.width + result
} else {
  this.newSize = this.beforeChangeSize.width - result
}

Затем присвойте элементу newSize

const [element] = this.$refs[`element-${this.beforeChangeSize.col}`]
element.style.width = `${newSize}px`
this.beforeChangeSize = {}

Сбросить стиль текущей кнопки изменения размера

const [resizeButton] = this.$refs[`resize-${this.beforeChangeSize.col}`]
resizeButton.style.left = 'auto'

Сбросьте beforeChangeSize на пустой объект.

this.beforeChangeSize = {};

Полный код

Песочница



Спасибо за чтение 🎉