javascript

Элегантная реализация Long Press обработчика с помощью CSS анимации

  • воскресенье, 24 ноября 2024 г. в 00:00:07
https://habr.com/ru/articles/860704/

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

▎Зачем нужен Long Press?

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

▎Стандартное решение

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

/* HTML */
<button id="btn">Click / Long Press</button>
/* JavaScript */
const btn = document.getElementById('btn');
const clickHandler = () => console.log('Сlick Handler');
const longPressHandler = () => console.log('Long Press Handler');

let timerId, longPressed

btn.onmousedown = () => {
  longPressed = false
  timerId = setTimeout(() => {
    longPressed = true
    longPressHandler();
  }, 200);
}

btn.onclick = () => {
  if (!longPressed) clickHandler();
  clearTimeout(timerId);
}

btn.onmouseleave = () => {
  clearTimeout(timerId);
}

Демонстрация на CodePen

Как мы видим, код достаточно громоздкий; необходимо использование таймера и глобальных переменных. Также нужно учитывать эффект, когда пользователь нажал на элемент, а отпустил его вне границ — такое поведение не должно вызывать ни один из наших обработчиков. Здесь это отслеживается с помощью слушателя mouseleave.

Проблема реализации только с помощью JavaScript заключается в том, что, как правило, после длительного нажатия будет следовать применение каких-либо стилей к элементу, а значит, нам придется писать дополнительный код на CSS.

▎Универсальное решение

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

/* CSS */
#btn {
  transition: background-color 1s 200ms; /* animation for long press */
}

#btn:active {
  animation-name: interruptClick;
  animation-delay: 200ms;
  animation-fill-mode: forwards;
  background-color: PaleTurquoise; /* styles for long press */
}

#btn:not(:hover){
  animation-play-state: paused;
}

@keyframes interruptClick {
  to {
    pointer-events: none;
  }
}
/* HTML */
<button id="btn">Click / Long Press</button>
/* JavaScript */
const btn = document.getElementById('btn');

btn.onclick = () => console.log('Сlick Handler');

btn.onanimationend = () => console.log('Long Press Handler');

Демонстрация на CodePen

Здесь мы видим, что логика определения долгого и короткого нажатия реализована полностью на CSS. С помощью JavaScript мы только устанавливаем слушателей. При долгом нажатии будет применена отдельная анимация стилизации кнопки (transition: background-color).

▎Как это работает?

1. CSS-анимация для реализации обработчиков:

:active: Этот псевдокласс позволяет применять свойства анимации только в момент удержания кнопки.

animation-name: Определяем @keyframes анимацию с одним ключевым кадром.

pointer-events: Ключевой кадр содержит свойство, управляющее тем, как элемент реагирует на события указателя (например, клики мышью). Клик считается завершенным, когда происходит и нажатие, и отпускание кнопки мыши на одном и том же элементе. Мы используем это свойство со значением “none”, чтобы предотвратить событие клика.

animation-delay: Анимация начинается через 200 миллисекунд после нажатия; это минимальное время удержания, необходимое для его определения как длительного.

animation-fill-mode: Время продолжительности анимации не указано, но благодаря значению “forwards” все свойства ключевого кадра будут оставаться активными до окончания удержания.

:not(:hover): Если пользователь нажал на кнопку, а затем отпустил за её границами, то анимация interruptClick будет остановлена с помощью свойства animation-play-state: paused, и ни один из обработчиков не будет вызван.

2. JavaScript слушатели:

onclick: Этот слушатель соответствует короткому нажатию.

onanimationend: Этот слушатель соответствует поведению долгого нажатия и срабатывает, когда заканчивается анимация interruptClick.

▎Преимущества данного подхода

• Универсальность: Все визуальные эффекты при длинном нажатии можно реализовать только с помощью CSS.

• Простота: Этот метод не требует сложных библиотек или дополнительных зависимостей.

• Эффективность: Использование CSS-анимаций позволяет избежать лишних вычислений в JavaScript, что может улучшить производительность.

• Чистота кода: Код остается чистым и легко читаемым.

▎Заключение

Эта реализация демонстрирует, как можно использовать возможности CSS в сочетании с JavaScript и как их взаимодействие позволяет писать более лаконичный код. 

Я протестировал это решение в нескольких браузерах, на компьютере и мобильном устройстве. Перед использованием кода в продакшене рекомендую провести дополнительное тестирование.