Я пишу веб-приложение, которое должно поддерживать как мышь, так и сенсорные взаимодействия. Для тестирования я использую устройство с сенсорным экраном в Windows 7. Я попытался прослушать события касания в последних версиях Firefox и Chrome и получил следующие результаты:

При касании Firefox запускает касание и соответствующее событие мыши. Chrome запускает touchstart/mousedown, touchend/mouseup пары, но mousemove срабатывает очень странным образом: один / два раза, пока touchmove.

Все события мыши обрабатываются как всегда.

Есть ли способ одновременно управлять мышью и касаться экрана на современных сенсорных экранах? Если Firefox запускает пару событий касания и мыши, что происходит в touchmove с mousemove в Chrome? Должен ли я перевести все события мыши на прикосновение или наоборот? Я надеюсь найти правильный способ создания адаптивного интерфейса.

15
johnny 26 Янв 2013 в 01:32

5 ответов

Лучший ответ

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

Вы можете сделать что-то вроде этого:

(function () {
    if ('ontouchstart' in window) {
        window.Evt = {
            PUSH : 'touchstart',
            MOVE : 'touchmove',
            RELEASE : 'touchend'
        };
    } else {
        window.Evt = {
            PUSH : 'mousedown',
            MOVE : 'mousemove',
            RELEASE : 'mouseup'
        };
    }
}());

// and then...

document.getElementById('mydiv').addEventListener(Evt.PUSH, myStartDragHandler, false);

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

var mydiv = document.getElementsById('mydiv');
mydiv.addEventListener('mousemove', myMoveHandler, false);
mydiv.addEventListener('touchmove', function (e) {
    // stop touch event
    e.stopPropagation();
    e.preventDefault();

    // translate to mouse event
    var clkEvt = document.createEvent('MouseEvent');
    clkEvt.initMouseEvent('mousemove', true, true, window, e.detail, 
                 e.touches[0].screenX, e.touches[0].screenY, 
                 e.touches[0].clientX, e.touches[0].clientY, 
                 false, false, false, false, 
                 0, null);
    mydiv.dispatchEvent(clkEvt);

    // or just handle touch event
    myMoveHandler(e);
}, false);
7
lupatus 12 Июл 2013 в 09:58

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

Вместо этого вы всегда должны слушать как события касания, так и события мыши, но при этом вызывайте warnDefault () для событий касания, которые вы обрабатываете, чтобы предотвратить запуск (теперь избыточных) событий мыши для них. Подробнее см. http://www.html5rocks.com/en/mobile/touchandmouse/.

18
Rick Byers 25 Апр 2013 в 14:02

Я использовал этот помощник jQuery для привязки событий касания и нажатия.

(function ($) {
$.fn.tclick = function (onclick) {
    this.bind("touchstart", function (e) { onclick.call(this, e); e.stopPropagation(); e.preventDefault(); });
    this.bind("click", function (e) { onclick.call(this, e); });   //substitute mousedown event for exact same result as touchstart         
    return this;
  };
})(jQuery);
0
Joshua 19 Май 2013 в 21:25

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

var lastEvent = 3  ;
var MOUSE_EVENT = 1;
var TOUCH_EVENT = 2 ; 
 

element.addEventListener('touchstart', function(event)
		{
			 
			if (lastEvent === MOUSE_EVENT  )
			{
				var time =  Date.now() - eventTime  ;  
				if ( time > 5000 ) 
				{
					eventTime = Date.now() ;
					lastEvent = TOUCH_EVENT ;
					interactionStart(event) ;
				} 
			}
			else 
			{
				lastEvent = TOUCH_EVENT ; ;
				eventTime = Date.now() ;
				interactionStart(event)  ; 
			}
			 
		}) ;	

		element.addEventListener('mousedown',	function(event)
		{
			
			if (lastEvent === TOUCH_EVENT  )
			{
				var time =  Date.now() - eventTime  ;	
				if ( time > 5000 ) 
				{
					eventTime = Date.now() ;
					lastEvent = MOUSE_EVENT ;
					interactionStart(event) ;
				} 
			}
			else 
			{
				lastEvent=  MOUSE_EVENT ; 
				eventTime = Date.now() ;
				interactionStart(event)  ; 
			}
		}) ;  

function interactionStart(event) // handle interaction (touch or click ) here.
{...}

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

1
FutureSci 6 Июн 2016 в 18:04

Я нашел эту тему, потому что у меня похожая и более сложная проблема:

Предположим, мы создаем область прокрутки с поддержкой js со стрелками СЛЕДУЮЩАЯ / ПРЕДЫДУЩАЯ, которую мы хотим не только реагировать на события касания и мыши, но и многократно запускать их, пока пользователь продолжает нажимать на экран или удерживать нажатой кнопку мыши!

Повторение событий заставило бы мою следующую кнопку продвинуться на 2 позиции вместо одной!

С помощью замыканий все кажется возможным:

(1) Сначала создайте самозапускающуюся функцию для изоляции переменных:

 (function(myScroll, $, window, document, undefined){
 ...
 }(window.myScroll = window.myScroll || {}, jQuery, window, document));

(2) Затем добавьте свои личные переменные, которые будут содержать внутреннее состояние из setTimeout():

/*
 * Primary events for handlers that respond to more than one event and devices  
 * that produce more than one, like touch devices.
 * The first event in browser's queue hinders all subsequent for the specific 
 * key intended to be used by a handler.
 * Every key points to an object '{primary: <event type>}'.
 */
var eventLock = {};
// Process ids based on keys.
var pids = {};
// Some defaults
var defaults = {
   pressDelay: 100 // ms between successive calls for continuous press by mouse or touch
}

(3) Функция блокировки событий:

function getEventLock(evt, key){
   if(typeof(eventLock[key]) == 'undefined'){
      eventLock[key] = {};
      eventLock[key].primary = evt.type;
      return true;
   }
   if(evt.type == eventLock[key].primary)
      return true;
   else
      return false;
}
function primaryEventLock(evt, key){
   eventLock[key].primary = evt.type;
}

(4) Присоедините свои обработчики событий:

function init(){
   $('sth').off('mousedown touchstart', previousStart).on('mousedown touchstart', previousStart);
   $('sth').off('mouseup touchend', previousEnd).on('mouseup touchend', previousEnd);
   // similar for 'next*' handlers
}

Запуск событий mousedown и touchstart вызовет двойные вызовы для обработчиков на устройствах, которые поддерживают оба (вероятно, сначала срабатывает касание). То же самое относится к mouseup и touchend.

Мы знаем, что устройства ввода (фактически, целые графические среды) генерируют события последовательно, поэтому нас не волнует, какой из них срабатывает первым, пока специальный ключ установлен в private eventLock.next.primary и eventLock.previous.primary для первых событий, полученных от обработчиков next*() и previous*() соответственно.

Этот ключ является типом события, поэтому второе, третье и т. Д. Событие всегда являются проигравшими, они не получают блокировку с помощью функций блокировки eventLock() и {{X1} } .

(5) Вышесказанное можно увидеть в определении обработчиков событий:

function previousStart(evt){
   // 'race' condition/repetition between 'mousedown' and 'touchstart'
   if(!getEventLock(evt, 'previous'))
      return;

   // a. !!!you have to implement this!!!
   previous(evt.target);

   // b. emulate successive events of this type
   pids.previous = setTimeout(closure, defaults.pressDelay);

   // internal function repeats steps (a), (b)
   function closure(){
      previous(evt.target);
      primaryEventLock(evt, 'previous');
      pids.previous = setTimeout(closure, defaults.pressDelay);
   }
};
function previousEnd(evt){
      clearTimeout(pids.previous);
};

Аналогично для nextStart и nextEnd.

Идея состоит в том, что тот, кто придет после первого (касание или мышь), не приобретет блокировку с помощью function eventLock(evt, key) и остановится на этом.

Единственный способ открыть эту блокировку - запустить обработчики событий прекращение *End() на шаге (4): previousEnd и nextEnd.

Я также решаю проблему сенсорных устройств, подключенных в середине сеанса, очень умным способом: я заметил, что непрерывное нажатие дольше, чем defaults.pressDelay, вызывает последовательные вызовы функции обратного вызова only для основное событие в то время (причина в том, что никакой обработчик конечных событий не завершает вызов callabck)!

touchstart event
closure
closure
....
touchend event

Я определяю primary устройство, которое использует пользователь, поэтому все, что вам нужно сделать, это просто нажать и удерживать кнопку, и ваше устройство сразу же становится primary с помощью primaryEventLock(evt, 'previous') внутри закрытие!

Также обратите внимание, что время выполнения previous(event.target) должно быть меньше, чем defaults.pressDelay.

(6) Наконец, давайте представим init() для глобальной области видимости:

myScroll.init = init;

Вам следует заменить вызов previous(event.target) на имеющуюся проблему: fiddle.

Также обратите внимание, что в (5b) есть решение другого популярного вопроса как мы передаем аргументы в функцию, вызванную из setTimeout() , т.е. {{X1 }} отсутствует механизм передачи аргументов.

0
centurian 25 Июн 2018 в 13:36