Spec-Zone.ru › Web APIs

События касания

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

Интерфейсы событий касания — это относительно низкоуровневые API, которые могут использоваться для поддержки специфичных для приложения многоточечных взаимодействий, таких как жесты с двумя пальцами. Многоточечное взаимодействие начинается, когда палец (или стилус) впервые касается сенсорной поверхности. Другие пальцы могут затем коснуться поверхности и, необязательно, перемещаться по сенсорной поверхности. Взаимодействие заканчивается, когда пальцы удаляются с поверхности. Во время этого взаимодействия приложение получает события касания на начальной, перемещающейся и конечной фазах.

События касания похожи на события мыши, за исключением того, что они поддерживают одновременные касания в разных местах на сенсорной поверхности. Интерфейс TouchEvent обобщает все активные точки касания. Интерфейс Touch, который представляет собой отдельную точку касания, включает информацию, такую как положение точки касания относительно области просмотра браузера.

Определения

Поверхность

Чувствительная к касанию поверхность. Это может быть экран или трекпад.

Точка касания

Точка контакта с поверхностью. Это может быть палец (или локоть, ухо, нос, что угодно, но обычно палец) или стилус.

Интерфейсы

TouchEvent

Представляет событие, которое происходит при изменении состояния касаний на поверхности.

Touch

Представляет собой отдельную точку контакта между пользователем и сенсорной поверхностью.

TouchList

Представляет группу касаний; это используется, когда у пользователя, например, несколько пальцев на поверхности одновременно.

Пример

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

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

Создание холста

<canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
  Your browser does not support canvas element.
</canvas>
<br />
Log:
<pre id="log" style="border: 1px solid #ccc;"></pre>
#log {
  height: 200px;
  width: 600px;
  overflow: scroll;
}

Настройка обработчиков событий

При загрузке страницы будет вызвана функция startup(), показанная ниже. Она устанавливает все обработчики событий для нашего элемента <canvas>, чтобы мы могли обрабатывать события касания по мере их возникновения.

function startup() {
  const el = document.getElementById("canvas");
  el.addEventListener("touchstart", handleStart);
  el.addEventListener("touchend", handleEnd);
  el.addEventListener("touchcancel", handleCancel);
  el.addEventListener("touchmove", handleMove);
  log("Initialized.");
}

document.addEventListener("DOMContentLoaded", startup);

Отслеживание новых касаний

Мы будем отслеживать касания в процессе.

const ongoingTouches = [];

При возникновении события touchstart, указывающего на то, что произошло новое касание поверхности, вызывается функция handleStart() ниже.

function handleStart(evt) {
  evt.preventDefault();
  log("touchstart.");
  const el = document.getElementById("canvas");
  const ctx = el.getContext("2d");
  const touches = evt.changedTouches;

  for (let i = 0; i < touches.length; i++) {
    log(`touchstart: ${i}.`);
    ongoingTouches.push(copyTouch(touches[i]));
    const color = colorForTouch(touches[i]);
    log(`color of touch with id ${touches[i].identifier} = ${color}`);
    ctx.beginPath();
    ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false); // a circle at the start
    ctx.fillStyle = color;
    ctx.fill();
  }
}

Это вызывает event.preventDefault(), чтобы предотвратить дальнейшую обработку браузером события касания (это также предотвращает доставку события мыши). Затем мы получаем контекст и извлекаем список измененных точек касания из свойства события TouchEvent.changedTouches.

После этого мы итерируем по всем объектам Touch в списке, помещая их в массив активных точек касания и рисуя начальную точку для рисования в виде маленького круга; мы используем линию шириной 4 пикселя, поэтому круг радиусом 4 пикселя будет хорошо отображаться.

Рисование при перемещении касаний

Каждый раз, когда один или несколько пальцев перемещаются, доставляется событие touchmove, что приводит к вызову нашей функции handleMove(). В этом примере её задача — обновить кэшированную информацию о касаниях и нарисовать линию от предыдущего положения к текущему положению каждого касания.

function handleMove(evt) {
  evt.preventDefault();
  const el = document.getElementById("canvas");
  const ctx = el.getContext("2d");
  const touches = evt.changedTouches;

  for (let i = 0; i < touches.length; i++) {
    const color = colorForTouch(touches[i]);
    const idx = ongoingTouchIndexById(touches[i].identifier);

    if (idx >= 0) {
      log(`continuing touch ${idx}`);
      ctx.beginPath();
      log(
        `ctx.moveTo( ${ongoingTouches[idx].pageX}, ${ongoingTouches[idx].pageY} );`,
      );
      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
      log(`ctx.lineTo( ${touches[i].pageX}, ${touches[i].pageY} );`);
      ctx.lineTo(touches[i].pageX, touches[i].pageY);
      ctx.lineWidth = 4;
      ctx.strokeStyle = color;
      ctx.stroke();

      ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // swap in the new touch record
    } else {
      log("can't figure out which touch to continue");
    }
  }
}

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

Это позволяет получить координаты предыдущего положения каждого касания и использовать соответствующие методы контекста для рисования сегмента линии, соединяющего два положения.

После рисования линии мы вызываем Array.splice(), чтобы заменить предыдущую информацию о точке касания текущей информацией в массиве ongoingTouches.

Обработка окончания касания

Когда пользователь поднимает палец с поверхности, отправляется событие touchend. Мы обрабатываем это, вызвав функцию handleEnd() ниже. Её задача — нарисовать последний сегмент линии для каждого завершенного касания и удалить точку касания из списка текущих касаний.

function handleEnd(evt) {
  evt.preventDefault();
  log("touchend");
  const el = document.getElementById("canvas");
  const ctx = el.getContext("2d");
  const touches = evt.changedTouches;

  for (let i = 0; i < touches.length; i++) {
    const color = colorForTouch(touches[i]);
    let idx = ongoingTouchIndexById(touches[i].identifier);

    if (idx >= 0) {
      ctx.lineWidth = 4;
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
      ctx.lineTo(touches[i].pageX, touches[i].pageY);
      ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8); // and a square at the end
      ongoingTouches.splice(idx, 1); // remove it; we're done
    } else {
      log("can't figure out which touch to end");
    }
  }
}

Это очень похоже на предыдущую функцию; единственные реальные отличия заключаются в том, что мы рисуем небольшой квадрат для обозначения конца и что при вызове Array.splice() мы удаляем старую запись из текущего списка касаний, не добавляя обновлённую информацию. Результатом является прекращение отслеживания этой точки касания.

Обработка отменённых касаний

Если палец пользователя заходит на область пользовательского интерфейса браузера или касание по какой-либо причине должно быть отменено, отправляется событие touchcancel, и мы вызываем функцию handleCancel() ниже.

function handleCancel(evt) {
  evt.preventDefault();
  log("touchcancel.");
  const touches = evt.changedTouches;

  for (let i = 0; i < touches.length; i++) {
    let idx = ongoingTouchIndexById(touches[i].identifier);
    ongoingTouches.splice(idx, 1); // remove it; we're done
  }
}

Поскольку идея состоит в немедленном прерывании касания, мы удаляем его из текущего списка касаний, не рисуя конечный сегмент линии.

Вспомогательные функции

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

Выбор цвета для каждого касания

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

function colorForTouch(touch) {
  let r = touch.identifier % 16;
  let g = Math.floor(touch.identifier / 3) % 16;
  let b = Math.floor(touch.identifier / 7) % 16;
  r = r.toString(16); // make it a hex digit
  g = g.toString(16); // make it a hex digit
  b = b.toString(16); // make it a hex digit
  const color = `#${r}${g}${b}`;
  return color;
}

Результат этой функции — строка, которая может использоваться при вызове функций <canvas> для установки цветов рисования. Например, для значения Touch.identifier 10, результирующая строка — "#a31".

Копирование объекта касания

В некоторых браузерах (например, мобильный Safari) объекты касания повторно используются между событиями, поэтому лучше копировать свойства, которые вас интересуют, а не ссылаться на весь объект.

function copyTouch({ identifier, pageX, pageY }) {
  return { identifier, pageX, pageY };
}

Поиск текущего касания

Функция ongoingTouchIndexById() ниже сканирует массив ongoingTouches , чтобы найти касание, соответствующее заданному идентификатору, а затем возвращает индекс этого касания в массиве.

function ongoingTouchIndexById(idToFind) {
  for (let i = 0; i < ongoingTouches.length; i++) {
    const id = ongoingTouches[i].identifier;

    if (id === idToFind) {
      return i;
    }
  }
  return -1; // not found
}

Отображение происходящего

function log(msg) {
  const container = document.getElementById("log");
  container.textContent = `${msg} \n${container.textContent}`;
}

Результат

Вы можете проверить этот пример на мобильных устройствах, коснувшись области ниже.

Примечание: В более общем смысле, пример будет работать на платформах, которые предоставляют события касания. Вы можете проверить это на настольных платформах, которые могут имитировать такие события:

  • В Firefox включите «имитацию касаний» в режиме отзывчивого дизайна (возможно, потребуется перезагрузить страницу).
  • В Chrome используйте режим эмуляции устройств и установите тип устройства на тот, который отправляет события касания.

Дополнительные советы

Этот раздел содержит дополнительные советы по обработке событий касания в вашем веб-приложении.

Обработка кликов

Поскольку вызов preventDefault() для события touchstart или первого события touchmove в серии предотвращает запуск соответствующих событий мыши, обычно вызывают preventDefault() на touchmove вместо touchstart. Таким образом, события мыши могут по-прежнему срабатывать, и такие вещи, как ссылки, продолжат работать. В качестве альтернативы, некоторые фреймворки переопределяют события касания в события мыши для той же цели. (Этот пример упрощен и может привести к странному поведению. Он предназначен только для справочных целей.)

function onTouch(evt) {
  evt.preventDefault();
  if (
    evt.touches.length > 1 ||
    (evt.type === "touchend" && evt.touches.length > 0)
  )
    return;

  const newEvt = document.createEvent("MouseEvents");
  let type = null;
  let touch = null;

  switch (evt.type) {
    case "touchstart":
      type = "mousedown";
      touch = evt.changedTouches[0];
      break;
    case "touchmove":
      type = "mousemove";
      touch = evt.changedTouches[0];
      break;
    case "touchend":
      type = "mouseup";
      touch = evt.changedTouches[0];
      break;
  }

  newEvt.initMouseEvent(
    type,
    true,
    true,
    evt.originalTarget.ownerDocument.defaultView,
    0,
    touch.screenX,
    touch.screenY,
    touch.clientX,
    touch.clientY,
    evt.ctrlKey,
    evt.altKey,
    evt.shiftKey,
    evt.metaKey,
    0,
    null,
  );
  evt.originalTarget.dispatchEvent(newEvt);
}

Вызов preventDefault() только для второго касания

Один из способов предотвращения таких ситуаций, как pinchZoom, на странице — вызов preventDefault() при втором касании в серии. Это поведение не определено в спецификации событий касания и приводит к различным результатам в разных браузерах (например, iOS предотвратит масштабирование, но всё ещё позволит панорамирование двумя пальцами; Android позволит масштабирование, но не панорамирование; Opera и Firefox в настоящее время предотвращают всё панорамирование и масштабирование). В настоящее время не рекомендуется полагаться на какое-либо конкретное поведение в этом случае, а вместо этого полагаться на мета-viewport для предотвращения масштабирования.

Спецификации

Спецификация
Touch Events

Совместимость с браузерами

Настольные Мобильные
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на IOS Samsung Internet WebView Android
Touch_events 18 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 18 6 14 2 1.0 4.4
item 18 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 18 6 14 2 1.0 4.4
length 18 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 18 6 14 2 1.0 4.4
Настольные Мобильные
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на IOS Samsung Internet WebView Android
TouchEvent
48Chrome поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
79Edge поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
Нет
35Opera поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
Нет
48Chrome Android поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
46
35Opera Android поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
3.2
5.0Samsung Internet поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
48WebView Android поддерживает только следующие свойства touchEventInit: touches, targetTouches, changedTouches.
Touch_events 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
altKey 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
changedTouches 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
ctrlKey 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
metaKey 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
shiftKey 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
targetTouches 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
touches 22 79 52
18–24Удалено в bug 888304 из-за проблем совместимости с веб-сайтами.
15 Нет 25 6 14 3.2 1.5 4.4
END_OF_DOCUMENT_MARKER
Настольный Мобильный
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on IOS Samsung Internet WebView Android
Touch 48 79
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
35 Нет 48 6 35 ≤3 5.0 48
Touch_events 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 ≤3 1.5 4.4
altitudeAngle Нет Нет Нет Нет Нет Нет Нет Нет 10 Нет Нет
azimuthAngle Нет Нет Нет Нет Нет Нет Нет Нет 10 Нет Нет
clientX 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
clientY 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
force 3822–47 ≤79
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
2515–34 Нет 3825–47 6 2514–34 10 3.01.5–5.0 384.4–47
identifier 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
pageX 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
pageY 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
radiusX 3822–47 ≤79
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
2515–34 Нет 3825–47 6 2514–34 10 3.01.5–5.0 384.4–47
radiusY 3822–47 ≤79
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
2515–34 Нет 3825–47 6 2514–34 10 3.01.5–5.0 384.4–47
rotationAngle 4322–47 ≤79
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
3015–34 Нет 4325–47 6 3014–34 10 4.01.5–5.0 434.4–47
screenX 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
screenY 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
target 22 ≤18
52Этот интерфейс доступен только при обнаружении сенсорного устройства ввода.
18–24Удалено в bug 888304 из-за проблем совместимости с веб-стандартами.
15 Нет 25 6 14 10 1.5 4.4
touchType Нет Нет Нет Нет Нет Нет Нет Нет 10 Нет Нет

api.Touch

Таблицы BCD загружаются только в браузере

api.TouchEvent

Таблицы BCD загружаются только в браузере

api.TouchList

Таблицы BCD загружаются только в браузере

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API