Масштабирование и панорамирование в 69 строчках JavaScript

Масштабирование и панорамирование в 69 строчках JavaScript

Возможность масштабировать элементы страницы и детально рассматривать их – это очень крутой пользовательский опыт. Существует множество готовых библиотек с подобной функциональностью, но сегодня мы напишем собственный велосипед на чистом JavaScript! Зачем?

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

В результате мы получим очень маленькую (всего 69 строчек кода!), простую и удобную библиотечку для масштабирования и панорамирования.

Разметка и стили

Создадим HTML-страницу и разместим на ней элемент-контейнер ( #container ). Внутрь поместим рабочую область ( .area ), которую мы и будем непосредственно масштабировать и панорамировать.

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

Добавим также немного стилей для оформления страницы:

Для body устанавливаем overflow: hidden . Это нужно, чтобы избежать переполнения страницы и появления прокрутки при чрезмерном увеличении элемента.

Также добавим рамку для визуального обозначения рабочей области ( .area ) и немного облагородим демо-контент (классы .circle , .rectangle и .text-area ).

Скрипт библиотеки

Код самой библиотеки будет располагаться в файле renderer.js . Экспортируем из модуля главную функцию renderer :

Она принимает базовые параметры:

  1. minScale – минимальный масштаб;
  2. maxScale – максимальный масштаб;
  3. element – DOM-элемент, с которым будут производиться манипуляции;
  4. scaleSensitivity – коэффициент чувствительность масштабирования, по умолчанию 10.

В замыкании функции создается объект состояния – state , который хранит настройки и совершенные над элементом преобразования (поле transformation ).

Из функции возвращается объект с набором методов. При этом возможности масштабирования и панорамирования разделены на отдельные функции-конструкторы – makeZoom и makePan , которые мы разберем чуть позже. Конструкторы получают общий объект состояния и возвращают отдельный набор методов для взаимодействия с ним.

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

Трансформации

Все манипуляции с элементом будут производиться через изменение свойства transform . Для этого используем CSS-функцию matrix , которой нужно передать правильные параметры масштаба ( scale ) и сдвига ( translateX и translateY ):

Вспомогательная функция getMatrix просто формирует шаблонную строку правильного формата, которую нужно установить в свойство style.transform элемента.

Панорамирование

При панорамировании должно изменяться положение элемента на странице, то есть производиться его сдвиг. Функция pan принимает текущее состояние элемента ( state ), а также новые координаты. Затем она обновляет состояние, прибавляя новый сдвиг к текущему положению и обновляет свойство style элемента.

Теперь реализуем два метода:

  1. panBy – простой сдвиг на указанные координаты;
  2. panTo – сдвиг с одновременным масштабированием.

При сдвиге с масштабированием координаты элемента нужно скорректировать.

Масштабирование

Для изменения размера элемента нам потребуется несколько вспомогательных функций для расчетов:

Метод getScale рассчитывает новый масштаб на основе предыдущего значения, минимального и максимального ограничений ( minScale , maxScale ) и коэффициентов ( scaleSensitivity , deltaScale ).

Метод getTranslate рассчитывает новый сдвиг на основе масштаба и текущей и предыдущей позиции.

А вот и реализация функции makeZoom :

Она возвращает только один метод zoom , предназначенный для масштабирования элемента. Он получает координаты курсора, а также параметр deltaScale – коэффициент, который определяет направление масштабирования ( 1 для увеличения, -1 для уменьшения).

Функция вычисляет новые параметры трансформации и обновляет свойство style элемента.

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

Главный файл

Кроме того мы сделаем главный файл приложения index.js :

Клиентский код создает экземпляр renderer и передает ему базовую конфигурацию:

  • элемент, размер которого будет изменяться;
  • минимальный и максимальный масштаб;
  • коэффициент чувствительности масштабирования.

Затем устанавливаются слушатели событий мыши и в нужный момент вызываются нужные методы:

  • для масштабирования используйте колесико мыши или сенсорную панель, зажав клавишу CTRL.
  • для перемещения – перемещайте мышь или используйте тачпад, зажав клавишу SHIFT.
  • Двойной щелчок мыши восстановит исходное состояние элемента.

Тестирование

Проверим реализованные функции с помощью библиотеки Mocha:

Перед каждым тестом ( beforeEach ) создается объект _element с дефолтными значениями.

Кейсы тестирования для удобства вынесены в отдельный файл renderer.testCases.js .

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