Опубликован: 10.04.2013 | Доступ: свободный | Студентов: 482 / 15 | Длительность: 16:52:00
Специальности: Программист
Лекция 3:

Ввод данных и сенсоры

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >

Захват указателя

Обычно, при событиях касания и поднятия указателя, устанавливают или снимают захват указателя элементом. Доступны следующие методы для поддержки этих действий каждым элементом DOM. Они применимы к каждому конкретному pointerId.

Таблица 3.4.
Метод Описание
msSetPointerCapture Захватывает pointerId для элемента, таким образом, события указателя поступают к этому элементу и не вызываются для других элементов (даже если указатель мыши находится вне этого элемента, внутри другого). MSGotPointerCapture так же будет вызвано для элемента.
msReleasePointerCapture Прекращает захват, вызывает событие MSLostPointerCapture .
msGetPointerCapture Возвращает элемент, захвативший указатель, если таковой имеется (в противном случае возвращает null).

Мы можем увидеть в примере "Ввод данных: обработка события указателя DOM", где устанавливается захват указателя в событии MSPointerDown и указатель освобождается в событии MSPointerUp:

this.MSPointerDown = function (evt) {	
canvas.msSetPointerCapture(evt.pointerId);
// ...	
};	

this.MSPointerUp = function (evt) {
canvas.msReleasePointerCapture(evt.pointerId);
// ...	
};	
 

События жестов

Первое, что стоит узнать о событиях MSGesture*, это то, что они не вызываются автоматически, как события click и MSPointer*, и недостаточно просто добавить прослушиватель и завершить тем дело (как для click!). Вместо этого вам нужно выполнить некоторые настройки, которые сообщат системе, как именно должны происходить жесты, и вам нужно исползовать MSPointerDown для того, чтобы связать настройки жеста с конкретным PointerId. Это усложняет схему работы, делая возможным работу приложения с несколькими различными жестами, независимую их обработку, что похоже на то, как работают с событиями указателей. Представьте, например, приложение-головоломку (в некоторой мере оно представлено в одном из примеров "Примеры жестов" ниже), которое позволяет нескольким пользователям сидеть вокруг сенсорного экрана размером со стол и каждому из них работать с отдельной частью головоломки. Используя жесты, каждый из игроков может управлять отдельной частью (или двумя!), перемещать ее, вращать, возможно масштабировать для того, чтобы увидеть ее покрупнее, и, конечно, пробовать поместить кусок головоломки в нужное место. Для приложений Магазина Windows, написанных на JavaScript, полезно то, что приращения манипуляции для сконфигурированных элементов – для переноса, вращения и масштабирования – даются в координатном пространстве родительского элемента, что означает, что довольно просто перенести манипуляции в CSS-трансформации и таким образом сделать манипуляции видимыми. Коротко говоря, это означает большую гибкость там, где она нужна вам. Если вам это не нужно, вы можете использовать жесты простейшим способом. Посмотрим, как все это работает.

Первый шаг к приему событий жестов заключается в создании объекта MSGesture (http://msdn.microsoft.com/en-us/library/windows/apps/hh968035.aspx) и в связывании его с элементом, для которого вы хотите принимать события. В упражнении PointerEvents этому элементу дано имя divElement, вам нужно сохранить этот элемент в свойстве жеста target и сохранить объект жеста в свойстве элемента gestureObject для использования его событием MSPointerDown:

var gestureObject = new MSGesture();
 gestureObject.target = divElement;
 divElement.gestureObject = gestureObject;
 

Установив эти связи, вы затем можете добавить прослушиватель события, как обычно. Пример кпоказывает полный набор из шести событий жестов:

 divElement.addEventListener("MSGestureTap", gestureTap);
divElement.addEventListener("MSGestureHold", gestureHold);

divElement.addEventListener("MSGestureStart", gestureStart);	
divElement.addEventListener("MSGestureChange", gestureChange);
divElement.addEventListener("MSGestureEnd", gestureEnd);	
divElement.addEventListener("MSInertiaStart", inertiaStart);	

Однако сейчас работа еще не завершена. Если это все, что вы сделаете в своем коде, вы еще не получите этих событий, так как каждый жест нужно связать с указателем. Это делается в обработчике события MSPointerDown:

function pointerDown(e) {	
//Связываем данный указатель с целевым жестом
e.target.gestureObject.addPointer(e.pointerId);
}

Для того чтобы получить возможность выполнять жесты вращения и изменения масштабирования с помощью колеса мыши (а это следует сделать), добавьте обработчик для события wheel, установите pointerId для этого события в 1 (фиксированное значение для колесика мыши) и отправьте то, что получилось в ваш обработчик MSPointerDown:

 divElement.addEventListener("wheel", function (e) {	
e.pointerId = 1;  // Фиксированный pointerId для MouseWheel
pointerDown(e);	
});	

Теперь события жестов готовы к поступлению в данный элемент. (Помните, что события колесика мыши, сами по себе, транслируются, Ctrl+колесо мыши – это масштабирование, Shift+Ctrl+колесо мыши – вращение). Более того, если возникнет дополнительное событие MSPointerDown для того же самого элемента с различными значениями pointerId, метод addPointer включит этот новый указатель в жест. Это автоматически активирует жесты изменения масштаба и вращения, которые основаны на нескольких точках касания.

Если вы запустите упражнение PointerEvents (установив флаги Ignore Mouse Events (Игнорировать события мыши) и Ignore Pointer Events (Игнорировать события указателя)), и начнете касаться экрана, касаться, удерживая палец на экране, выполнять касание и короткое перетаскивание (с помощью сенсорного экрана или мыши), вы увидите выходные данные, похожие на те, что приведены на рис.3.2

Упражнение PointerEvents, выходные данные для событий жестов (скриншот слегка обрезан для выделения деталей)

Рис. 3.2. Упражнение PointerEvents, выходные данные для событий жестов (скриншот слегка обрезан для выделения деталей)

Снова хочу отметить, что события жестов вызываются в ответ на последовательность событий указателя, предлагая высокоуровневую интерпретацию низкоуровневых событий указателя. В процессе интерпретации происходит дифференцирование событий касания/удержания от событий начала/изменения/окончания, выясняется, как и когда началось событие MSInertiaStart, и что предпринимает распознаватель жестов, когда объекту MSGesture передаются несколько точек.

Начиная с жеста одного указателя, первый аспект дифференцирования – это порог перемещения указателя (pointer movement threshold). Когда распознаватель жестов обнаруживает событие MSPointerDown, он начинает наблюдать за событиями MSPointerMove для того, чтобы видеть, остаются ли они внутри порогового расстояния, которое является эффективной границей для событий касания и удерживания. Это предназначено для того, чтобы игнорировать небольшое дрожание мыши или точки касания, как показано на рисунке ниже (я бы сказал, что здесь это несколько преувеличено!), когда происходит касание, небольшое перемещение, после чего указатель поднимается, генерируется событие MSGestureTap:


Horizontal threshold (Горизонтальный порог перемещения)

Vertical threschold (Вертикальный порог перемещения)

Many events (Множество событий)

Различить события MSGestureTap и MSGestureHold позволяет временной порог (time threshold): MSGestureTap происходит, когда за событием MSPointerDown следует событие MSPointerUp в пределах временного порога.

MSGestureHold происходит, когда за событием MSPointerDown следует событие MSPointerUp с превышением временного порога. В подобном случае, один раз, при пересечении временного порога, вызывается событие MSGestureHold с eventArgs.detail установленным в 1 (MSGESTURE_FLAG_BEGIN). При условии, что указатель все еще находится в пределах порога перемещения, событие MSGestureHold вызывается еще раз, когда происходит MSPointerUp, с eventArgs.detail установленным в 2 (MSGESTURE_FLAG_END). Вы можете увидеть эти детали в двух первых событиях на рис. 3.2. выше.

Флаги жестов в значении eventArgs.detail сопровождаются множеством других свойств, касающихся расположения и перемещения в объекте eventArgs, как показано в следующей таблице:

Таблица 3.5.
Свойства Описание
screenX, screenY Координаты X и Y центральной точки жеста, спозиционированные
clientX, clientY Координаты X и Y центральной точки жеста, спозиционированные относительно пользовательской области приожения.
offsetX, offsetY Координаты X и Y центральной точки жеста, спозиционированные
translationX, translationY Перенос по осям X и Y.
velocityX, velocityY Скорость перемещения по осям X и Y.
scale Коэффициент масштабирования (процентное изменение масштаба).
expansion Скорость расширения области манипуляции.
rotation Угол поворота в радианах.
velocityAngular Угловая скорость в радианах.
detail Содержит флаги жеста, которые описывают состояние события жеста. Эти флаги определены как значения в eventArgs:
  • eventArgs.MSGESTURE_FLAG_NONE (0): Указывает на текущий жест, такой, как MSGestureChange где представлены изменения координат.
  • eventArgs.MSGESTURE_FLAG_BEGIN (1): Начало последовательности жестов. Если взаимодействие включает в себя единственное событие, такое, как MSGestureTap, и флаг MSGESTURE_FLAG_BEGIN, и флаг MSGESTURE_FLAG_END будут установлены (значение 3).
  • eventArgs.MSGESTURE_FLAG_END (2): Конец последовательности жестов. Опять же, если взаимодействие включает в себя единственный жест, такой, как MSGestureTap, и флаг MSGESTURE_FLAG_BEGIN, и флаг MSGESTURE_FLAG_END будут установлены (значение 3).
  • eventArgs.MSGESTURE_FLAG_CANCEL (4): Жест был отменен.
hwTimestamp Отметка времени, когда указатель был назначен системой при поступлении входных данных от устройства.

Многие из этих свойств становятся гораздо интереснее, когда указатель выходит за пределы порога перемещения, после чего уже не произойдет события касания (tap) или удерживания (hold). Вместо этого, как только указатель пересечет порог, будет выполнено событие MSGestureStart, после чего возможен вызов событий MSGestureChange, их может не быть вовсе, но обычно их довольно много. В конце вызывается одно событие MSGestureEnd:


Обратите внимание на то, что если указатель не пересечет порог перемещения достаточно долго для вызова первого MSGestureHold с флагом MSGESTURE_FLAG_BEGIN, но затем указатель переместится за пределы порога, MSGestureHold будет вызван второй раз, с флагами MSGESTURE_FLAG_CANCEL | MSGESTURE_FLAG_END в eventArgs.detail (значение 6), за ним последует MSGestureStart с флагом MSGESTURE_FLAG_BEGIN. Подобная последовательность позволяет дифференцировать событие удержания (hold) от жестов прокрутки или перетаскивания, даже если пользователь какое-то время удерживал элемент на месте.

События MSGestureStart, MSGestureChange, и MSGestureEnd все вместе задают манипуляцию (manipulation) для элемента, с которым связан жест, когда указатель контактирует с элементом в течение манипуляции. Технически это означает, что указатель больше не перемещается, когда он освобождается.

Если указатель продолжает перемещаться после того, как он освобожден, тогда мы переключаемся с манипуляции на движение по инерции (inertial motion). В этом случае вызывается событие MSInertiaStart, оно показывает, что указатель продолжает несмотря на то, что контакт с элементом потерян, или указатель поднят. В результате, вы продолжаете получать события MSGestureChange до тех пор, пока движение не будет завершено:


С концептуальной точки зрения разница между манипуляцией и движением по инерции проиллюстрирована на рис. 3.3. Кривые, показанные здесь, не обязательно отражают реальные изменения между сообщениями. Если указатель переместился вдоль зеленой линии и болше не двигается после отпускания указателя, мы видим последовательность жестов, которая описывает манипуляцию. Если указатель отпущен при движении, мы видем событие MSInertiaStart между сериями событий MSGestureChange, и последовательность событий продолжается в виде оранжевой линии.

Концептуальное представление манипуляции (зеленая линия) и движения по инерции (оранжевая линия).

Рис. 3.3. Концептуальное представление манипуляции (зеленая линия) и движения по инерции (оранжевая линия).

  • Перемещение (Movement)
  • Время (Time)
  • Обнаружен жест (Gesture detected)
  • Манипуляция (Manipulation)
  • Инерция (Inertia)
  • Точка освобождения инерции (Inertia release point)
  • Перемещение следует за указателем (Movement follows pointer)
  • Точка освобождения манипуляции (Manipulation release point)
  • Перемещение продолжается, без контакта с указателем (Continued movement without pointer contact)
  • Перемещение завершено (Movement complete)

Возвращаясь к рис. 3.2., где выпадающий список Show (Показ) был установлен в значение Velocity (Скорость), выходные данные для событий MSGestureChange включали значения eventArgs.velocity*. В течение манипуляции, скорость может меняться с любой частотой, в зависимости от того, как движется указатель. Однако когда начинается движение по инерции, скорость постепенно падает до нуля, и тогда происходит событие MSGestureEnded. Количество событий изменения зависит от того, сколько времени нужно для того, чтобы продолжать замедляющееся, вплоть до остановки, движение, но если вы просто перемещаете элемент по экрану с помощью этих событий, несущих информацию об изменениях, пользователь увидит приятную плавную анимацию. Вы можете поэкспериментировать с этим в упражнении PointerEvents, используя выпадающий список Show для того, чтобы посмотреть, как на другие свойства позиционирования влияют различные жесты при манипуляциях и движении по инерции.

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >