- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Код

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

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

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

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

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 [15] объекта 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-е [16], а, благодаря github pages [17], вы можете посмотреть в действии [18] пример карты с инерцией при передвижении. Пример лучше всего смотреть в Google Chrome, так как под различные браузеры и устройства он не оптимизировался, это не является основной целью статьи. Что же касается самого OpenLayers 3, то библиотека находится в стадии активной разработки и использовать ее в “боевых” приложениях пока не стоит (имеются проблемы при работе с тач-устройствами [19], а также со стабильностью самого API [20]). Тем не менее, пожелаем разработчикам выдержки, вдохновения и хороших pull request-ов в столь полезный для пользователей проект.

Автор: AndreyGeonya

Источник [21]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/25023

Ссылки в тексте:

[1] API карт 2GIS: http://api.2gis.ru/about/features/#maps

[2] инерционный скролл: http://cubiq.org/dropbox/iscroll4/examples/simple/

[3] инерционное движение карусели фотографий: http://www.annefrank.org/en/Subsites/Timeline/#!/en/Subsites/Timeline/

[4] инерционное перемещение карты 2GIS: http://maps.api.2gis.ru/

[5] Bing: http://www.bing.com/maps/

[6] Google: https://maps.google.com.ua/

[7] OpenLayers 3: https://github.com/openlayers/ol3

[8] документации: https://github.com/AndreyGeonya/ol3#run-the-examples-in-debug-mode

[9] Closure library: https://developers.google.com/closure/library/?hl=ru

[10] ol.interaction.DragPan: https://github.com/AndreyGeonya/ol3/blob/master/src/ol/interaction/dragpaninteraction.js

[11] ol.interaction.DragPanInertia: https://github.com/AndreyGeonya/ol3/blob/master/src/ol/interaction/dragpaninertiainteraction.js

[12] была добавлена: https://github.com/AndreyGeonya/ol3/blob/master/src/ol/polyfill/requestanimationframepolyfill.js

[13] requestAnimationFrame: https://developer.mozilla.org/en-US/docs/DOM/window.requestAnimationFrame

[14] Erik Möller: http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

[15] createInteractions: https://github.com/AndreyGeonya/ol3/blob/master/src/ol/map.js#L1083

[16] на github-е: https://github.com/AndreyGeonya/ol3

[17] github pages: http://pages.github.com/

[18] посмотреть в действии: http://andreygeonya.github.com/inertiaDemo/

[19] работе с тач-устройствами: https://github.com/openlayers/ol3/issues/109

[20] стабильностью самого API: https://github.com/openlayers/ol3/pull/96#issuecomment-11949939

[21] Источник: http://habrahabr.ru/post/166061/