Гибридные приложения и проклятие задержки в 300 мс

Когда мы разобрались со стандартами HTML5 и современными браузерами на компьютерах, мобильные устройства поставили нас на место.  Мобильные браузеры по-прежнему остаются "диким западом" для веб-разработки (кстати, за это я их и люблю). Так как они появились относительно недавно, мы по клочкам собираем опыт и знания, чтобы понять, как создавать хорошие веб-приложения для мобильных устройств. Когда речь заходит о времени отклика в мобильных браузерах, одной из самых больших проблем становится задержка в 300 мс после каждого касания пользователя.

Почему в мобильных браузерах есть задержка в 300 мс?

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

Ниже я продемонстрирую, как работает задержка:

  1. Пользователь касается экрана, браузер отмечает событие touchstart
  2. Пользователь убирает палец с экрана, браузер отмечает событие touchend
  3. Браузер ждет 300 мс, чтобы понять, будет ли пользователь нажимать еще раз
  4. Если пользователь не нажмет снова, браузер посылает событие click

Гибридные приложения не ощущаются как "мобильные приложения"!

Эта фраза стала слишком часто звучать в соответствующих кругах. Но это понятно, так как пользователя не волнует, "почему" приложение не ощущается как обычная мобильная программа!  Я полагаю, что задержка в 300 мс способствует распространению этого мнения. Я соглашусь, что исторически мобильные браузеры имели проблемы с производительностью, и что отдельно эта тема вызывала тысячи споров, но как по мне, чтобы гибридные приложения ощущались как нативные, проще всего будет убрать эту досадную задержку.

Вы меня убедили, теперь давайте уберем задержку

Теперь мы знаем, почему браузеры используют задержку, и что именно в ней причина, почему наши гибридные приложения не ощущаются как нативные программы. Так давайте уберем ее! Слишком все просто, не так ли?

До этого я говорил о гибридных приложениях лишь в общих чертах, но теперь я остановлюсь подробнее на Ionic, который держится на плечах AngularJS. Если коротко, то AngularJS — отличный фреймворк для создания мобильных приложений. Поэтому мы используем Ionic, так как он отлично с ним сочетается. Но довольно непросто объединить безумный мир мобильных браузеров и всю магию Angular и удалить эту задержку в 300 мс без вызова двойных, четверных нажатий или даже отсутствия таковых для каждого сценария в каждом браузере.

Ionic и ngTouch в Angular

Разработчики на Angular сразу зададут вопрос: "Что насчет модуля ngTouch? Разве он не убирает задержку?" Когда речь идет о создании простого приложения на Angular с возможностью использования сенсорного экрана, ngTouch служит отличным вариантом. Но здесь упускается несколько моментов. При объединении с Ionic возникает дублирование кода в больших масштабах, что приводит к еще большим проблемам.  И так как модуль не является частью ядра Angular, и есть дополнительная зависимость, которую не стоить включать, мы решили пойти в сторону полного отказа от его использования, и вот почему:

  • ngTouch замещает ngClick и выполняет его работу, чтобы убрать задержку для этого элемента. Но он не убирает задержку для всех других элементов без атрибута ng-click. Текстовые области, поля ввода, ссылки, кнопки, метки и т. д. могут находиться на странице без атрибута ng-click, поэтому при нажатии на них задержка сохраняется.
  • Директива ngTouch  имеет весь необходимый код для обнаружения касания. Она добавляет глобальные приемники событий для touchstart и click, и для каждого элемента с ng-click она добавляет приемники следующих событий: touchstart, touchmove, touchcancel, touchend, mousedown, mousemove и mouseup.  Это то место, где возникает дублирование.
  • Ionic уже имеет систему событий (которая является ответвлением из hammer.js) для обнаружения виртуальных событий и движений вроде касания, проведения пальцем по экрану в разные стороны, удержания и перетаскивания. Система событий Ionic может легко использоваться повторно самим фреймворком и разработчиками в отличие от простого привязывания этой логики к элементам с атрибутом ng-click.
  • Внесу ясность: ngTouch  — отличный дополнительный модуль для Angular, и без использования Ionic я бы его рекомендовал. Но с Ionic мы уйдем от простого обнаружения касаний для отдельных элементов с ручным назначением ng-click и сделаем это для всех элементов автоматически (без создания обработчиков событий для каждого элемента).

Что насчет Fastclick.js?

Если вы не используете Ionic или AngularJS, то я настоятельно рекомендую fastclick.js. Это проверенное, широко используемое средство, которое отлично работает на многих устройствах. Но при объединении с Angular возникали некоторые проблемы, поэтому мы решили отдельно это отметить. Fastclick.js — отличный плагин, но я не рекомендую его для создания приложения на AngularJS.

Почему вы заключаете input в label?

Малоизвестная особенность HTML — заключение элемента input в элемент label. Вы связываете эти два элемента, и id input и атрибут for label становятся одним и тем же. Это быстрый и простой трюк, который позволяет сразу сделать большую активную область для input, что жизненно важно для удобства использования сенсорных устройств. Этот прием отлично подходит для полей с вводом текста, так как окружающая область и даже ее фактический label будет отвечать на касания, и фокус сразу перейдет на поле ввода (без задержки в 300 мс).

Кроме того, для переключателей, чекбоксов и флажков, заключение их inputs в label упрощает дизайн области касания с использованием псевдокласса CSS :checked в input. Этот ловкий трюк  позволяет не использовать JavaScript для дизайна элементов. Например, если вы нажмете на флажок в списке, все изменения в отображении будут совершены с помощью CSS. Здесь не будет добавления обработчиков событий и удаления имен класса (поэтому не будет и манипуляций с DOM).

Так как в Ionic убрать задержку?

Есть ряд простых приемов, которые помогут убрать задержку, но тут есть довольно коварный враг — злостный "призрачный клик".  Призрачный клик может возникнуть во многих сценариях. Самый распространенный из них: когда Ionic выполняет click при touchend, но при этом браузер спустя 300 мс отправляет еще один click. Получается click click. А это плохо-плохо.

Кроме того, часто случается так, что пользователь нажимает на объект, и сразу срабатывают обычные события из целевого элемента, но браузер предполагает, что нажали на элемент "около" того места, где пользователь коснулся экрана. Браузер думает: "Эй, я знаю, что ты нажал на этот div и все, но ты был довольно близок, чтобы щелкнуть по той кнопке, поэтому я предполагаю, что ты хотел нажать и на нее". В таких случаях click запускается для div и для кнопки, что служит большим источником проблем.

Вдобавок ко всему, помните, как мы заключаем inputs в label, чтобы не добавлять обработчики событий и производить еще больше манипуляций с DOM? В некоторых браузерах и labеl и input запускают событие click, что ведет к двойным или даже четверным нажатиям. Эти ошибки браузера также ведут к некоторым проблемам с производительностью, так как одинаковый код для одного взаимодействия приходится выполнять множество раз.

Чтобы избежать призрачных кликов, мы отслеживаем все координаты x и y для touchends и clicks. Если последующие координаты касания и нажатия будут находиться в пределах одной области и одного времени, то мы примем его за то же нажатие и проигнорируем все последующие призрачные клики.

Итак, подведем итог. Ionic предотвращает призрачные клики следующим образом:

  • Запоминает координаты x, y для каждого касания/нажатия и предотвращает появление повторяющихся кликов, которые происходят в той же области в то же время.
  • Устанавливает последнее время, когда элемент был нажат, и игнорирует любые последующие нажатия, которые сразу следуют за первым.
  • Не позволяет labels распространять их clicks по DOM, что не касается исходного label, в котором input должен запустить click.

Удаление задержки на уровне браузера

Разработчики браузеров понимают, что двойное нажатие для увеличения масштаба не всегда необходимо, и появляются более новые методы удаления задержки. В Chrome 32 недавно убрали задержку, отключив масштабирование. Это пока временное новшество в Chrome и Firefox для Android.

Но эта особенность приводит к еще одной проблеме, которая может сопровождать решения по простому обнаружению касаний. Когда браузеры запускают событие click без задержки в 300 мс, click может произойти до touchend, что по-другому извратит эту проблему. К счастью, Ionic проверяет и такие сценарии.

Заключение

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

 

Комментарии