Делаем сенсорную прокрутку быстрой по умолчанию

Дэйв Тапуска
Dave Tapuska

Мы знаем, что скорость реагирования на прокрутку имеет решающее значение для взаимодействия пользователя с веб-сайтом на мобильных устройствах, однако прослушиватели событий касания часто вызывают серьезные проблемы с производительностью прокрутки. Chrome решает эту проблему, позволяя прослушивателям событий касания быть пассивными (передавая параметр {passive: true} в addEventListener() ) и предоставляя API событий указателя . Это отличные функции для внедрения нового контента в модели, не блокирующие прокрутку, но разработчикам иногда бывает трудно понять и внедрить их.

Мы считаем, что Интернет по умолчанию должен быть быстрым, и разработчикам не нужно разбираться в загадочных деталях поведения браузера. В Chrome 56 мы по умолчанию устанавливаем пассивные прослушиватели касаний в тех случаях, когда это чаще всего соответствует намерению разработчика. Мы считаем, что, сделав это, мы сможем значительно улучшить взаимодействие с пользователем, оказывая при этом минимальное негативное влияние на сайты.

В редких случаях это изменение может привести к непреднамеренной прокрутке. Обычно эту проблему легко решить, применив стиль touch-action : none к элементу, где прокрутка происходить не должна. Читайте дальше, чтобы узнать, как узнать, пострадали ли вы, и что вы можете с этим поделать.

Предыстория: Отменяемые события замедляют работу вашей страницы.

Если вы вызовете метод предотвращения Default() в событиях touchstart или first touchmove , вы предотвратите прокрутку. Проблема в том, что чаще всего слушатели не будут вызывать preventDefault() , но браузеру необходимо дождаться завершения события, чтобы быть в этом уверенным. Определенные разработчиком «пассивные прослушиватели событий» решают эту проблему. Когда вы добавляете событие касания с объектом {passive: true} в качестве третьего параметра в обработчике событий, вы сообщаете браузеру, что прослушиватель touchstart не будет вызывать preventDefault() , и браузер может безопасно выполнять прокрутку без блокировки слушатель. Например:

window.addEventListener("touchstart", func, {passive: true} );

Вмешательство

Наша основная мотивация — сократить время, необходимое для обновления дисплея после того, как пользователь коснулся экрана. Чтобы понять, как используются Touchstart и TouchMove, мы добавили метрики, позволяющие определить, как часто происходит блокировка прокрутки.

Мы изучили процент отменяемых событий касания, которые были отправлены корневому целевому объекту (окну, документу или телу), и определили, что около 80% этих прослушивателей концептуально пассивны, но не были зарегистрированы как таковые. Учитывая масштаб этой проблемы, мы заметили прекрасную возможность улучшить прокрутку без каких-либо действий разработчика, сделав эти события автоматически «пассивными».

Это побудило нас определить наше вмешательство следующим образом: если целью прослушивателя touchstart или touchmove является window , document или body , мы по умолчанию passive со true . Это означает, что код типа:

window.addEventListener("touchstart", func);

становится эквивалентным:

window.addEventListener("touchstart", func, {passive: true} );

Теперь вызовы preventDefault() внутри прослушивателя будут игнорироваться.

На графике ниже показано время, затраченное 1% верхних прокруток с момента, когда пользователь касается экрана для прокрутки, до момента обновления дисплея. Эти данные относятся ко всем веб-сайтам в Chrome для Android. До того, как вмешательство было включено, 1% прокруток занимал чуть более 400 мс. В бета-версии Chrome 56 это время сократилось до чуть более 250 мс; снижение примерно на 38%. В будущем мы надеемся сделать пассивное значение true по умолчанию для всех прослушивателей touchstart и touchmove , уменьшив это значение до уровня ниже 50 мс.

График времени прокрутки в топ-1%

Поломка и руководство

В подавляющем большинстве случаев поломок не будет. Но когда поломка все же происходит, наиболее распространенным симптомом является то, что прокрутка происходит тогда, когда вы этого не хотите. В редких случаях разработчики также могут заметить неожиданные события щелчка (когда в touchend прослушивателе отсутствовала preventDefault() ).

В Chrome 56 и более поздних версиях DevTools регистрирует предупреждение при вызове preventDefault() в событии, когда вмешательство активно.

touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

Ваше приложение может определить, может ли оно сталкиваться с этим в реальных условиях, проверив, оказал ли вызов preventDefault какой-либо эффект через свойство defaultPrevented .

Мы обнаружили, что подавляющее большинство затронутых страниц относительно легко исправить, если по возможности применить свойство CSS сенсорного действия . Если вы хотите запретить прокрутку и масштабирование в браузере внутри элемента, примените к нему touch-action: none . Если у вас есть горизонтальная карусель, рассмотрите возможность применения к ней touch-action: pan-y pinch-zoom чтобы пользователь мог прокручивать по вертикали и масштабировать как обычно. Правильное применение сенсорного действия уже необходимо в таких браузерах, как Edge для настольных компьютеров, которые поддерживают события указателя, а не события касания. Для мобильных Safari и более старых мобильных браузеров, которые не поддерживают сенсорное действие, ваши прослушиватели сенсорного ввода должны продолжать вызывать preventDefault даже если он будет игнорироваться Chrome.

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

  • Если ваш прослушиватель touchstart вызывает preventDefault() , убедитесь, что метод PreventDefault() также вызывается из связанных прослушивателей Touchend, чтобы продолжать подавлять генерацию событий щелчка и другое поведение касания по умолчанию.
  • Последним (и не рекомендуется) передать {passive: false} в addEventListener(), чтобы переопределить поведение по умолчанию. Имейте в виду, что вам нужно будет определить, поддерживает ли пользовательский агент EventListenerOptions .

Заключение

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

Хотя это по-прежнему необходимо для мобильного Safari, веб-сайты не должны полагаться на вызов метода preventDefault() внутри прослушивателей touchstart и touchmove , поскольку больше не гарантируется, что это будет соблюдаться в Chrome. Разработчикам следует применять свойство CSS touch-action к элементам, в которых прокрутка и масштабирование должны быть отключены, чтобы уведомлять браузер перед возникновением каких-либо событий касания. Чтобы подавить поведение касания по умолчанию (например, генерацию события щелчка), вызовите метод preventDefault() внутри прослушивателя touchend .