Браузер Android искажает события для двух нажатий в быстрой последовательности - PullRequest
9 голосов
/ 18 декабря 2011

Я пытаюсь использовать JavaScript и jQuery для захвата сенсорных событий.Но я наблюдаю очень странное поведение в веб-браузере на Android 2.3.2: всякий раз, когда я касаюсь экрана, а затем быстро нажимаю где-нибудь еще на экране, браузер:

  • на мгновение показываеторанжевая рамка и выделение по всему экрану, и
  • посылает мне неправильные события.

Оранжевая граница, кажется, является просто связанным симптомом того жеосновная проблема, так что я не слишком беспокоюсь об этом - на самом деле удобно знать, когда браузер все испортил.Что я действительно хочу знать, так это: как я могу последовательно получать правильные события касания для двух быстрых нажатий? Я считаю, что когда эта проблема будет решена, оранжевая граница также исчезнет.

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

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

Мой код jQuery довольно стандартный.Реализация функции log не важна;проблема в том, что браузер не вызывает его, когда должен.

el = $('#battle');
el.on('touchstart', function(event) {
  log(event);
  return event.preventDefault();
});
el.on('touchend', function(event) {
  return log(event);
});
el.on('touchcancel', function(event) {
  return log(event);
});
el.mousedown(function(event) {
  log(event);
  return event.preventDefault();
});
return el.mouseup(function(event) {
  return log(event);
});

Подробнее о явлениях, которые я описал изначально:

Оранжевая рамка и выделение: Это та же оранжевая рамка и выделение, которое браузер рисует вокруг гиперссылки при нажатии на нее.Но на странице нет гиперссылок, и браузер рисует эту оранжевую рамку вокруг всего экрана - или, более конкретно, вокруг внешнего <div id="battle">, к которому я подключаю события через jQuery.

Неправильные события: В моем touchstart обработчике событий я звоню event.preventDefault(), чтобы сообщить браузеру не прокручивать, не синтезировать события мыши и т. Д. Поэтому я ожидаю получить только touchstart иtouchend событий.И я делаю, для первого крана.Но вместо touchstart / touchend для второго касания я получаю все количество комбинаций событий касания, синтезированных событий мыши и случайных touchcancel для второго касания или даже повторяющихся событий для первого касания.Подробности ниже.

Такое поведение также имеет место только в очень особых обстоятельствах:

  • Первый сигнал должен быть коротким (менее ~ 200 мс).
  • Второй сигналдолжно произойти быстро после этого (менее чем ~ 450 мс после первого касания touchstart).
  • Второй отвод должен находиться на расстоянии не менее 150 пикселей от первого касания (измеряется по диагонали от координат первого касания).touchstart).
  • Если я удаляю код, перехватывающий mousedown и mouseup, оранжевые прямоугольники больше не появляются.Тем не менее, сенсорные события иногда все еще искажены.

Что я имею в виду под искаженными событиями, вот что я вижу.Когда я пишу «1:», это означает, что события предназначены для координат первого касания;«2:» означает координаты второго касания.Я видел следующие схемы событий (проценты показывают, сколько раз каждый из них появлялся после 100 испытаний):

  • (50%) 1: touchstart 1: touchend 1: mousedown 1: mouseup (короткая задержка)) 2: mousedown 2: mouseup
  • (35%) 1: touchstart 1: касание 2: touchstart 1: mousedown 1: mouseup 2: touchend
  • (10%) 1: touchstart 1:touchend 2: touchstart 1: mousedown 1: mouseup 2: touchcancel (короткая задержка) 2: mousedown 2: mouseup
  • (3%) 1: touchstart 1: touchend 2: touchstart 2: touchend (короткая задержка) 1: mousedown 1: mouseup
  • (2%) 1: touchstart 1: touchend 1: mousedown 1: mouseup (и вообще ничего для второго нажатия)

Некоторые комбинации событий появляются чаще всего в зависимости от того, насколько быстро я нажимаю, но я не совсем прибил схему.(По второму пункту выше, скорее всего, появятся два быстрых, четких удара, в то время как более быстрый подход с меньшим акцентом на четкость кажется более вероятным первым пунктом. Но я не определил конкретные временные числа, которые приводят ккаждому.) Точно так же «короткие задержки», указанные выше, могут составлять от ~ 150 мс до ~ 400 мс;Я также не перепроектировал весь шаблон там.

Если я не подключу mousedown и mouseup, распределение будет примерно таким:

  • (40%) 1: сенсорный запуск 1: касание 2: сенсорный запуск 2: касание
  • (35%) 1: сенсорный запуск 1: касание 2: касание 2: касание (фактическое желаемое поведение)
  • (25%) 1: запуск прикосновения 1: прикосновение (и вообще ничего для второго касания)

Так что, если я не перехватываю события мыши,это работает треть времени;и если бы я хотел сделать вид, что touchcancel означало то же самое, что и touchend, я мог бы получить это до 75% времени.Но это все еще довольно отстойно.

Альтернативы, которые я уже пробовал:

  • Я пытался использовать jQuery Mobile vmousedown иvmouseup события , но они не всегда запускаются при втором касании, я подозреваю, из-за этой же странности лежащего в основе события.
  • Я мог бы просто полностью забыть о событиях касания и использовать только синтезированную мышьсобытий, но обычно между задержкой физического нажатия и доставкой события синтезированной мыши обычно есть задержка примерно в полсекунды, тогда как события касания происходят немедленно, поэтому я могу быть более отзывчивым.Я также хочу предотвратить прокрутку - это для полноэкранной игры, и я бы предпочел, чтобы пользователь случайно не прокручивал адресную строку назад и не блокировал часть игры - и делал preventDefault на touchstartобычно это достигается (хотя иногда второй тап действительно способен прокручивать экран, несмотря на мою preventDefault ... еще одну причину, по которой я хочу решить весь этот беспорядок событий).
  • Я пробовал стороннюю версиюВеб-браузер ( Dolphin ), но у него те же проблемы с событиями.Поэтому я предполагаю, что это, вероятно, проблема с тем, как базовый WebView доставляет события в сценарии.

Может кто-нибудь предложить способ изменить мой HTML, мои обработчики событий или что-то еще, чтобы надежнополучать события касания для двух быстрых нажатий подряд?

Ответы [ 5 ]

3 голосов
/ 20 декабря 2011

Попытавшись разработать мультитач-игры HTML5 в браузере Android (и попробовать их в других браузерах, совместимых с Android), я думаю, что браузер Android 2.x просто не поддерживает сенсорный ввод должным образом. Для начала, он не поддерживает мультитач, что делает некоторые виды игр неиграемыми. (Очевидно, что телефон поддерживает мультитач, потому что вы можете ущипнуть масштабирование и т. Д., Но браузер этого не делает.) Тогда есть много проблем с задержкой, касанием «залипания» и скоро. Я смутно помню, что читал что-то о драйверах телефона для сенсорных входов, которые на самом деле не работают с истинным мультитачем (то есть он может обнаруживать масштабирование в одно касание или пинч-масштабирование и все), но у меня нет ссылок, подтверждающих это ...

Судя по всему Android 4 (Ice Cream Sandwich) исправляет это. Так что вам, возможно, придется подождать Android 4, который скоро выйдет, и попробовать еще раз. Кроме того, Google объявил, что в будущем планирует заменить браузер Android мобильной версией Chrome, так что, надеюсь, что по крайней мере к тому времени наши проблемы с сенсорным вводом в браузере исчезнут.

1 голос
/ 20 декабря 2011

Это теория: не удалось тщательно протестировать

В соответствии с функцией init в коде soruce веб-просмотра , оператор setFocusable(true) всегда вызывается для веб-просмотра во время его инициализации.

Когда я попытался больше не фокусировать вид, используя setFocusable(false), ошибка больше не повторялась. Кажется, оранжевая коробка не появляется. Я тестировал это на маленьком телефоне Samsung с ОС 2.3.4. В чем я уверен, так это в том, что эта оранжевая коробка больше не появлялась.

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

Наконец, я не думаю, что мы можем управлять этим свойством из JavaScript (или мы можем?). Может быть, вы можете объявить, что конкретный элемент управления или весь документ не является входом или что-то подобное? Я только экстраполирую настолько, что это может быть ложным.

Редактировать: По поводу вашего комментария к вашему вопросу Я только что создал пустое приложение только с веб-представлением, которое загружает ваш URL после установки его свойства focusable в false. Пожалуйста, если у вас есть больше ресурсов для тестирования, я загрузлю его для вас, чтобы попробовать. Вот приложение

0 голосов
/ 23 декабря 2011

Вы пытались прикрепить событие напрямую?

$(document).ready(function(){
    var el, hypot, log, prev, resetTimeout, start, prevTouchX, prevTouchY;
    var resetTimeout = null;
    var start = null;
    var prev = null;
    var resetTimeout;

    var hypot = function(x1, y1, x2, y2) {
        var dx, dy, h;
        dx = x2 - x1;
        dy = y2 - y1;
        h = Math.sqrt(dx * dx + dy * dy);
        return Math.round(h * 10) / 10;
    };


    var logf = function(event) {
        var div, table, values, x, y, _ref, _ref2, _ref3, _ref4, _ref5, _ref6;
        if (event.type === "touchend"){
            x = prevTouchX;
            y = prevTouchY;
        } else {
            x = event.touches[0].pageX;
            y = event.touches[0].pageY;
            prevTouchX = x;
            prevTouchY = y;
        }
        div = $('.log :first');
        table = div.find('table');
        if (table.length === 0) {
            $('.log div:gt(1), .log hr:gt(0)').remove();
            table = $('<table border style="border-collapse: collapse;"><tr>\n<th rowspan="2">Event</th>\n<th rowspan="2">X</th>\n<th rowspan="2">Y</th>\n<th colspan="4">From start</th>\n<th colspan="4">From prev</th>\n</tr><tr>\n<th>&Delta;T (ms)</th>\n<th>&Delta;X</th>\n<th>&Delta;Y</th>\n<th>Distance</th>\n<th>&Delta;T (ms)</th>\n<th>&Delta;X</th>\n<th>&Delta;Y</th>\n<th>Distance</th>\n</tr></table>');
            div.append(table);
        }
        values = {
            time: event.timeStamp,
            x: x,
            y: y
        };
        if (start == null) {
            start = values;
        }
        if (prev == null) {
            prev = values;
        }
        table.append("<tr>\n<td>" + event.type + "</td>\n<td>" + x + "</td>\n<td>" + y + "</td>\n<td>" + (event.timeStamp - start.time) + "</td>\n<td>" + (x - start.x) + "</td>\n<td>" + (y - start.y) + "</td>\n<td>" + (hypot(x, y, start.x, start.y)) + "</td>\n<td>" + (event.timeStamp - prev.time) + "</td>\n<td>" + (x - prev.x) + "</td>\n<td>" + (y - prev.y) + "</td>\n<td>" + (hypot(x, y, prev.x, prev.y)) + "</td>\n</tr>");
        prev = values;

        if(resetTimeout !== null){
            window.clearTimeout(resetTimeout)
        }
        resetTimeout = window.setTimeout(function(){
            start = null;
            prev = null;
            $('.log').prepend('<hr/>');
        }, 1000);
    };
    var battle = document.getElementById("battle");
    battle.addEventListener("touchstart",logf,  false);
    battle.addEventListener("touchmove",function(e){logf(e);e.preventDefault();},  false);
    battle.addEventListener("touchend",logf,  false);
    battle.addEventListener("touchcancel",logf,  false);
});

(Извините, если код действительно неаккуратный, я не особо обращал внимание на функцию log, но я сделал некоторые незначительные изменения, так как она не запускалась корректно при моем событии touchend как event.touches [0] ] .pageX не определен на этом этапе. Кроме того, я обернул его в готовую функцию, потому что я просто ленивый :-P)

Поскольку это отслеживание только первого касания (event.touches [0]), вы, вероятно, можете внести некоторые коррективы для проверки мультитач, перейдя вниз по массиву касаний. Что я обнаружил на своем устройстве Android (пряник), так это то, что если у вас два пальца на экране одновременно, событие касания запускается только при отпускании последнего касания; то есть отпустить второй палец.

Кроме того, когда я подключил слушателей событий mousedown / mouseup, я получил то же самое, что вы сделали со всей оранжевой подсветкой.

Устройство, на котором я тестировал, было обновлением Samsung Droid Charge с обновлением OTA Gingerbread.

0 голосов
/ 21 декабря 2011

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

 $("...").bind("mousedown touchstart MozTouchDown", function(e) {
   if(e.originalEvent.touches && e.originalEvent.touches.length) {
    e = e.originalEvent.touches[0];
    } else if(e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
    e = e.originalEvent.changedTouches[0];
    }

// Handle mouse down
 });
0 голосов
/ 20 декабря 2011

Вы пробовали использовать события jQuery Mobile?Вы можете найти разделенные виджеты / плагины здесь:

https://github.com/jquery/jquery-mobile/tree/master/js

Вам, вероятно, понадобятся jquery.mobile.event.js и jquery.mobile.vmouse.js

Теперь вы можете просто привязать события крана, смахивания и т. Д. В jQuery.Или необходимо различать начало и конец сигнала?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...