Упрощение механизма масштабирования

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

Выгоды

Я бы не стал считать это узким местом такого же масштаба, как печально известное применение CSS-преобразований через Vue-реквизиты, которое я обсуждаю в другом посте, но это изменение уменьшает количество арифметических операций на настройку для каждого элемента. С десятками элементов на экране приветствуется каждая сэкономленная миллисекунда! ⏳

Но гораздо большее значение имеют два других преимущества:

  1. Это упрощает код.
  2. Это предотвращает отклонение элементов от их даты с течением времени.

Ключом к решению является транслятор положения.

Использование переводчика позиций

В самом начале разработки я ввел должность переводчика. Он переводит относительные позиции (даты, такие как 1516 год) в абсолютные позиции и наоборот. Я использовал транслятор положения, чтобы поместить вновь созданные временные события и маркеры времени в нужное место на экране. У него простая и понятная логика:

A: абсолютное положение (пиксели экрана)

R: относительное положение (дата вроде 1516 г.)

ZL: текущий уровень масштабирования

A0: абсолютная позиция даты 0 (короткая: нулевая временная шкала)

A = R * ZL + A0

R = (A — A0) / ZL

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

Ранее средство масштабирования рассчитывало новую позицию временного события или маркера, принимая старую позицию, положение указателя мыши и коэффициент масштабирования. Взглянем:

for (let i = 0; i < timeEvents.length; i++) {
  const distance =
  timeEvents[i].positionCenter - referencePosition) * zoomFactor;
  const newPosition = referencePosition + distance;
  timeEvents[i].positionCenter = newPosition;
}

Поскольку мы в любом случае вычисляем нулевую временную шкалу при каждом масштабировании, мы можем оставить все эти параметры в стороне и просто вызвать транслятор положения, чтобы получить новое абсолютное положение.

for (let i = 0; i < timeEvents.length; i++) {
  const newPosition =
  PositionTranslator.toAbsolutePosition(timeEvents[i].date);
}

Устранение дрейфа положения

Во время масштабирования нашей временной шкалы приложение выполняет множество вычислений с дробными числами. Поскольку эти числа представлены в виде десятичных дробей в JavaScript, мы теряем точность с течением времени. Абсолютная позиция элемента больше не будет соответствовать его фактической дате. Например. элемент с датой 1516 может иметь абсолютную позицию, которая, если мы переведем его обратно, будет соответствовать дате 1515.999.

Дрейф микроскопический, но я думаю, что при более длительном сеансе он будет значительным… Подождите! Разве мы не страдаем от потери точности с преобразователем положения? Да, но теперь у нас одинаковая потеря точности для каждого элемента.

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

Краткое содержание

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