Инерция в JavaScript на примере OL3

в 6:26, , рубрики: javascript, maps, Maps API, Блог компании ДубльГИС (2ГИС), метки: ,

Современный мир диктует разработчикам и дизайнерам довольно высокие стандарты качества и удобства использования веб-приложений. Как правило, хорошее впечатление о приложении складывается из множества мелочей, которые должны гармонично сочетаться между собой. Одной из таких мелочей может являться инерционное движение “драгабельных” (перетаскиваемых) объектов на странице — об этом мы и поговорим в рамках сегодняшней небольшой статьи. Фича особенно актуальна при взаимодействии пользователя с приложением посредством тач-устройств, так как размер экранов таких устройств ограничен, и передвигать объект из одной точки в другую хочется одним “легким движением руки”, а не многократными касаниями экрана.

Подобным вопросом мы когда-то задались в рамках разработки API карт 2GIS, а сегодня решили поделиться нашим скромным опытом.

Прежде чем рассматривать детали реализации инерции, давайте взглянем на уже готовые примеры:

Хорошо, теперь к делу.

Пространство для экспериментов

В качестве тестового примера добавим инерционное перемещение к одному из картографических API, у которого еще нет такой фичи. Таким образом мы: а) чему-то научимся; б) нанесем кому-то необратимую пользу.

Давайте искать столь “обделенное” API. Bing, Google? Нет, у этих ребят с перетаскиванием карты все в порядке. Посмотрим, как обстоят дела у OpenLayers 3. Да, наш “пациент”, над ним и будем ставить эксперименты.

Несмотря на молодость “подопытного”, установка трудностей не вызвала, все как в документации:

Инерция в JavaScript на примере OL3

Исходный код всех html, js и css файлов примеров можно найти в папке ol3/examples.

Наброски алгоритма

На самом деле, все довольно просто.

Подготовка к инерционному движению:

  • отлавливаем момент начала перетаскивания карты и записываем текущее время;
  • отлавливаем момент окончания перетаскивания (пользователь отпустил кнопку мышки) и в этот момент:
    • меряем продолжительность перетаскивания (в мс.);
    • меряем дистанцию, которая была пройдена по оси Х и по оси Y (в пикс.);
    • считаем скорость перетаскивания карты по оси Х и по оси Y (в пикс. за мс.):
      • скорость = дистанция / продолжительность.
    • считаем начальный импульс по оси Х и по оси Y:
      • импульс = масса * скорость, где масса — это просто константа.
  • вызываем метод, который отвечает за инерционное движение карты по окончанию перетаскивания, назовем его inertiaMove.

Метод inertiaMove вызывается раз в определенный период (каждых 16 мс., например). В этом методе мы:

  • сдвигаем центр карты на значение импульса по оси Х и по оси Y;
  • уменьшаем импульс, разделив его на коэффициент затухания (константа);
  • проверяем, не “скатился” ли импульс по оси Х и по оси Y до определенного предела (тоже константа), если “скатился” — останавливаем движение.

Инерция в JavaScript на примере OL3

“Экстренная” остановка перетаскивания. Карта должна принудительно перестать двигаться в двух случаях:

  • если пользователь кликнул мышкой по карте;
  • если пользователь изменил масштаб карты.

Код

Так как OpenLayers 3 сделан на основе Closure library, будем писать код по его правилам.

Взглянув на файловую структуру, мы увидим, что в каталоге interactions находятся все объекты, которые отвечают за взаимодействие с картой. Среди них есть объект ol.interaction.DragPan, отвечающий за перетаскивание, создадим и отнаследуем от него наш ol.interaction.DragPanInertia и реализуем в нем инерционное движение карты.

Также для удобства была добавлена реализация polyfill-а для метода requestAnimationFrame от Erik Möller.

Что касается принудительной остановки движения карты, делается это весьма простой подпиской на соответствующие события:

var eventType = ol.BrowserFeature.HAS_TOUCH ? goog.events.EventType.TOUCHEND : goog.events.EventType.MOUSEDOWN;

// mouse down
goog.events.listen(this._map.getViewport(), eventType, this.stopInertiaMove, false, this);

// zoom changed
goog.events.listen(this._map, ol.Object.getChangedEventType(ol.MapProperty.RESOLUTION), this.stopInertiaMove, false, this);

Для того, чтобы карта начала использовать реализованные нами объекты, их необходимо зарегистрировать в методе createInteractions объекта ol.Map.

Сначала подключаем нашу реализацию, заменив

goog.require('ol.interaction.DragPan');

на

goog.require('ol.interaction.DragPanInertia');

А потом строку

interactions.push(new ol.interaction.DragPan(ol.interaction.condition.noModifierKeys));

заменяем на

interactions.push(new ol.interaction.DragPanInertia(ol.interaction.condition.noModifierKeys));

Как вы уже наверное заметили, реализованный нами код лежит на github-е, а, благодаря github pages, вы можете посмотреть в действии пример карты с инерцией при передвижении. Пример лучше всего смотреть в Google Chrome, так как под различные браузеры и устройства он не оптимизировался, это не является основной целью статьи. Что же касается самого OpenLayers 3, то библиотека находится в стадии активной разработки и использовать ее в “боевых” приложениях пока не стоит (имеются проблемы при работе с тач-устройствами, а также со стабильностью самого API). Тем не менее, пожелаем разработчикам выдержки, вдохновения и хороших pull request-ов в столь полезный для пользователей проект.

Автор: AndreyGeonya

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js