Lit предоставляет удобный и декларативный подход к работе с DOM,
включая обработку событий касания (touch) и жестов. Использование
touch-событий особенно важно для мобильных интерфейсов, где пользователи
взаимодействуют с элементами через сенсорный экран. В основе работы с
touch лежат стандартные браузерные события: touchstart,
touchmove, touchend и
touchcancel. Lit позволяет интегрировать их напрямую через
директиву @event.
Пример: обработка touchstart и
touchend
import { LitElement, html, css } from 'lit';
class TouchDemo extends LitElement {
static styles = css`
div {
width: 200px;
height: 200px;
background-color: lightblue;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
touch-action: none;
}
`;
onTouchStart(e) {
console.log('Touch start:', e.touches.length);
this.shadowRoot.querySelector('div').style.backgroundColor = 'lightgreen';
}
onTouchEnd(e) {
console.log('Touch end');
this.shadowRoot.querySelector('div').style.backgroundColor = 'lightblue';
}
render() {
return html`
<div
@touchstart=${this.onTouchStart}
@touchend=${this.onTouchEnd}
>
Touch me
</div>
`;
}
}
customElements.define('touch-demo', TouchDemo);
Ключевой момент: директива
@touchstart=${this.onTouchStart} связывает метод компонента
с событием DOM, обеспечивая корректное связывание контекста
this.
touchmoveСобытие touchmove срабатывает при перемещении пальца по
экрану. Важно использовать e.preventDefault() для
предотвращения скролла страницы, если элемент должен перехватывать жест
полностью.
onTouchMove(e) {
e.preventDefault(); // предотвращает прокрутку
const touch = e.touches[0];
console.log(`Moved to (${touch.clientX}, ${touch.clientY})`);
}
Совет: Для производительных приложений стоит
оптимизировать обработку touchmove, используя
requestAnimationFrame для обновления DOM, чтобы избежать
лагов при частых событиях.
Жесты, такие как свайпы и зумы, можно реализовать через комбинацию touch-событий. Основная идея — хранить состояние касания между событиями:
class SwipeDemo extends LitElement {
startX = 0;
startY = 0;
onTouchStart(e) {
const touch = e.touches[0];
this.startX = touch.clientX;
this.startY = touch.clientY;
}
onTouchEnd(e) {
const touch = e.changedTouches[0];
const deltaX = touch.clientX - this.startX;
const deltaY = touch.clientY - this.startY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
console.log(deltaX > 0 ? 'Swipe right' : 'Swipe left');
} else {
console.log(deltaY > 0 ? 'Swipe down' : 'Swipe up');
}
}
render() {
return html`
<div
style="width:200px;height:200px;background:lightcoral;"
@touchstart=${this.onTouchStart}
@touchend=${this.onTouchEnd}
>
Swipe me
</div>
`;
}
}
customElements.define('swipe-demo', SwipeDemo);
Важное замечание: changedTouches
содержит только те пальцы, которые завершили взаимодействие, что делает
его предпочтительным для анализа финальной позиции жеста.
touch-action и предотвращение конфликтовCSS-свойство touch-action играет ключевую роль в
контроле жестов браузера:
none — блокирует стандартные жесты (скролл,
масштабирование).pan-x, pan-y — разрешает вертикальное или
горизонтальное скроллирование.manipulation — разрешает стандартные манипуляции,
блокируя только двойной тап.Пример использования:
div {
touch-action: none; /* полностью контролируем жесты через JS */
}
Это важно для корректной работы кастомных свайпов и мультитач-жестов.
Touch-события поддерживают несколько касаний одновременно. Объект
TouchList (e.touches) содержит все активные
точки касания.
Пример обработки зума двумя пальцами:
onTouchMove(e) {
if (e.touches.length === 2) {
const [t1, t2] = e.touches;
const distance = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY);
console.log('Pinch distance:', distance);
}
}
Используя начальное расстояние между пальцами при
touchstart, можно реализовать детектирование увеличения или
уменьшения масштаба элемента.
passive: true для событий, где не
вызывается preventDefault, чтобы ускорить скроллинг.touchmove.will-change: transform для элементов,
которые изменяются в результате жестов, чтобы GPU-ускорение уменьшало
лаги.Lit позволяет комбинировать touch-события с условным рендерингом и реактивными свойствами, что делает возможным динамическое включение или отключение обработки жестов в зависимости от состояния компонента.
Пример динамической блокировки свайпов:
html`
<div
style="touch-action: ${this.gesturesEnabled ? 'none' : 'pan-y'};"
@touchstart=${this.gesturesEnabled ? this.onTouchStart : null}
@touchend=${this.gesturesEnabled ? this.onTouchEnd : null}
>
Dynamic gestures
</div>
`
Такой подход позволяет создавать гибкие интерфейсы, адаптирующиеся под разные режимы работы.