Выбор кадра на основе фиксированного позиционирования

JavaScript

Недавний проект включает в себя небольшое требование для поддержки пакетных операций, и взаимодействие должно запускаться выбором поля. Изучив некоторую информацию, я обнаружил, что онлайн-решения в основном основаны на макете с абсолютным позиционированием.Если это решение предназначено для глобального (на теле) выбора кадра, оно все еще доступно. Но на самом деле это почти всегда подбор кадра под определенную область. Если это реализовано с абсолютным позиционированием, это сложнее, и необходимо настроить начало позиционирования. Далее представлена ​​реализация выбора кадра на основе фиксированного позиционирования.

Описание требования

  1. Удерживая левую кнопку мыши, переместите мышь, чтобы отобразить окно выбора.
  2. В процессе движения мыши элементы внутри рамки выделения подсвечиваются
  3. Отпустите левую кнопку мыши, появится окно редактирования, и все выбранные элементы можно будет обрабатывать в пакетном режиме.

выполнить

привязка события

Сначала отсортируйте события, которые необходимо использовать.
Удерживайте нажатой левую кнопку мыши, потому что нет собственного события нажатия левой кнопки мыши, поэтому используйте событие mousedown для имитации реализации setTimeout. Событие mousedown привязано к текущей области. Используйте флаговую переменную mouseOn, чтобы указать, следует ли начинать рисование.

handleMouseDown(e) {
  // 判断是否为鼠标左键被按下
  if (e.buttons !== 1 || e.which !== 1) return;
  this.settimeId = window.setTimeout(() => {
    this.mouseOn = true;
    // 设置选框的初始位置
    this.startX = e.clientX;
    this.startY = e.clientY;
  }, 300);
},
handleMouseUp(e) {
  //在mouseup的时候清除计时器,如果按住的时间不足300毫秒
  //则mouseOn为false
  this.settimeId && window.clearTimeout(this.settimeId)
  if (!this.mouseOn) return;
}

Чтобы переместить мышь, используйте событие mousemove. Поднимите мышь, используйте событие mouseup, обратите внимание, что событие подъема требуетпривязать к документу. Поскольку пользовательская операция выбора кадра не будет ограничена текущей областью, отпускание мыши в любом месте должно иметь возможность завершить рисование выбора кадра.

шатер рисунок

После того, как события прояснены, необходимо только заполнить конкретный рисунок и логику суждений в нескольких событиях. Давайте сначала посмотрим на логику рисования. В событии mousedown установите начальное положение выделения, то есть положение, в котором нажата мышь. Здесь мы пишем div заранее, чтобы представить бегущую строку.

<div class="promotion-range__select" ref="select"></div>
.promotion-range__select {
  background: #598fe6;
  position: fixed;
  width: 0;
  height: 0;
  display: none;
  top: 0;
  left: 0;
  opacity:.6;
  pointer-events: none;
}

Отобразить этот div после нажатия и установить начальное позиционирование

this.$refs.select.style.cssText = `display:block;
                                   left:${this.startX}px;
                                   top:${this.startY}px
                                   width:0;
                                   height:0;`;

С начальной позицией в событии mousemove установите ширину, высоту и положение области выделения.

handleMouseMove(e) {
  if (!this.mouseOn) return;
  const $select = this.$refs.select;
  const _w = e.clientX - this.startX;
  const _h = e.clientY - this.startY;
  //框选有可能是往左框选,此时框选矩形的左上角就变成
  //鼠标移动的位置了,所以需要判断。同理宽高要取绝对值
  this.top = _h > 0 ? this.startY : e.clientY;
  this.left = _w > 0 ? this.startX : e.clientX;
  this.width = Math.abs(_w);
  this.height = Math.abs(_h);
  $select.style.left = `${this.left}px`;
  $select.style.top = `${this.top}px`;
  $select.style.width = `${this.width}px`;
  $select.style.height = `${this.height}px`;
},

Если используется абсолютное позиционирование, необходимо откалибровать начало координат, что очень обременительно, когда в компоновке вложены несколько контейнеров относительного позиционирования. Использование фиксированного позиционирования избавляет от необходимости учитывать, к какому контейнеру оно относится.

Определить, что выбрано полем

//获取目标元素
const selList = document.getElementsByClassName(
  "promotion-range__item-inner"
);
const { bottom, left, right, top } = $select.getBoundingClientRect();
for (let i = 0; i < selList.length; i++) {
  const rect = selList[i].getBoundingClientRect();
  const isIntersect = !(
    rect.top > bottom ||
    rect.bottom < top ||
    rect.right < left ||
    rect.left > right
  );
  selList[i].classList[isIntersect ? "add" : "remove"]("is-editing");
}

Судя по тому, что используется getBoundingClientRect, ссылка на определение взята из MDN.

Возвращаемое значение — это объект DOMRect, представляющий собой набор прямоугольников, возвращаемый методом getClientRects() элемента, то есть набор границ CSS, связанных с элементом.

Объект DOMRect содержит набор доступных только для чтения свойств, описывающих границу — левую, верхнюю, правую и нижнюю в пикселях. Свойства, кроме ширины и высоты, относятся кверхний левый угол окна просмотраместо нахождения.

Из определения видно, что левое, верхнее, правое и нижнее значения, полученные в getBoundingClientRect, относятся к верхнему левому углу области просмотра, что согласуется с определением фиксированного позиционирования. Поэтому нам нужно только сравнить четыре значения позиционирования бегущей строки и выбранного элемента.

rect.top > bottom Элемент с рамкой находится над рамкой

rect.bottom

rect.right

rect.left > right Выбранный элемент кадра находится с правой стороны кадра

Исключением этих четырех случаев является пересечение области выделения и элемента, выбранного кадром. Добавьте класс к этим элементам div. Поскольку пользователю необходимо воспринимать элемент, выбранный кадром, во время процесса перемещения, описанный выше метод также должен выполняться в mousemove.

Оценив элемент, выбранный фреймом, в указателе мыши, установите для фрейма значение display: none.

Демонстрационный адрес функции

GitHub.com/Это сложно заблокировать/…

Ссылка на ссылку

woohoo.brief.com/afraid/5052 из 6 против 2…
developer.Mozilla.org/this-cn/docs/…
developer.Mozilla.org/this-cn/docs/…