9

JavaScript と jQuery を使用してタッチ イベントをキャプチャしようとしています。しかし、Android 2.3.2 の Web ブラウザーで非常に奇妙な動作が見られます。

  • 画面全体にオレンジ色の枠線とハイライトが一時的に表示されます
  • 間違ったイベントを送ります。

オレンジ色の境界線は、同じ根本的な問題に関連する症状のように見えるので、あまり心配していません。実際には、ブラウザーが問題を起こしていることを知ることができるので便利です。私が本当に知りたいのは、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、2 回目のタップでは、タッチ イベント、合成されたマウス イベント、およびtouchcancel2 回目のタップの不定期、または最初のタップの繰り返しイベントのすべての組み合わせを取得します。詳細は以下。

この動作も非常に特殊な状況でのみ発生します。

  • 最初のタップは短くする必要があります (~200ms 未満)。
  • その後、2 回目のタップはすぐに行う必要があります (最初のタップから 450 ミリ秒以内touchstart)。
  • 2 番目のタップは、最初のタップから少なくとも 150 ピクセル離れている必要があります (最初のタップの座標から対角線に沿って測定touchstart)。
  • と をフックするコードを削除するmousedownmouseup、オレンジ色の四角形は表示されなくなります。ただし、タッチ イベントはまだ文字化けすることがあります。

イベントが文字化けしているという意味で、私が見ているのは次のとおりです。「1:」と書くと、それはイベントが最初のタップの座標に対するものであることを意味します。「2:」は 2 番目のタップの座標を意味します。次のパターンのイベントが見られました (パーセンテージは、100 回の試行後に各イベントが発生した回数を示します)。

  • (50%) 1:touchstart 1:touchend 1:mousedown 1:mouseup (短い遅延) 2:mousedown 2:mouseup
  • (35%) 1:タッチスタート 1:タッチエンド 2:タッチスタート 1:マウスダウン 1:マウスアップ 2:タッチエンド
  • (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 (2回目のタップは何もしない)

タップする速さに応じて、いくつかのイベントの組み合わせがより頻繁に発生するように見えますが、パターンを完全に突き止めていません. (2 回のすばやく鮮明なタップは、上記の 2 番目の項目に分類される可能性が高いようですが、鮮明さをあまり重視しない、より迅速な発射アプローチは、最初の項目に分類される可能性が高いようです。同様に、上記の「短い遅延」は、約 150 ミリ秒から約 400 ミリ秒の範囲です。パターン全体のリバース エンジニアリングも行っていません。

mousedownと をフックしない場合mouseup、分布はおおよそ次のようになります。

  • (40%) 1:タッチスタート 1:タッチエンド 2:タッチスタート 2:タッチキャンセル
  • (35%) 1:touchstart 1:touchend 2:touchstart 2:touchend (実際に望ましい動作)
  • (25%) 1:touchstart 1:touchend (2回目のタップは何もしない)

したがって、マウス イベントをフックしないと、3 分の 1 の確率で動作します。touchcancelそして、それがと同じことを意味するふりをしても構わないと思っていればtouchend、最大 75% の確率でそれを得ることができました。しかし、それはまだかなり厄介です。

私がすでに試した代替案:

  • jQuery Mobileeventsvmousedownvmouseup使用してみましたが、2回目のタップで常にトリガーされるとは限りません。これと同じ根本的なイベントの奇妙さのためだと思います。
  • タッチ イベントを完全に忘れて、合成されたマウス イベントのみを使用することもできますが、通常、物理的なタップと合成されたマウス イベントの配信の間に約 0.5 秒の遅延がありますが、タッチ イベントは即時であるため、応答性が高くなります。 . また、スクロールを防止したい - これは全画面表示のゲームのためであり、ユーザーが誤ってアドレスバーをスクロールして戻ってゲームの一部をブロックしてしまうのを防ぎたい -preventDefault通常touchstartはそれを達成します (ただし、時折2回目のタップは、実際には画面をスクロールできますpreventDefault...このイベント全体の混乱を解決したいもう1つの理由)。
  • サードパーティの Web ブラウザ ( Dolphin )を試しましたが、イベントに関して同じ問題があります。したがって、おそらく、基になる WebView がイベントをスクリプトに配信する方法に問題があると思います。

HTML、イベントハンドラー、またはその他のものを変更して、2回連続してすばやくタップした場合のタッチイベントを確実に取得する方法を誰かが提案できますか?

4

5 に答える 5

3

Android ブラウザーでマルチタッチ HTML5 ゲームを開発しようとして (そして他の Android 互換ブラウザーでも試してみましたが)、Android 2.x のブラウザーはタッチ入力を適切にサポートしていないと思います。まず、マルチタッチがサポートされていないため、一部のゲームがプレイできなくなります。(もちろんスマホはピンチズーム等ができるのでマルチタッチに対応していますが、ブラウザは対応していません。)その後、遅延やタッチの「固着」などの問題が多いです。タッチ入力用の携帯電話のドライバーが真のマルチタッチで実際には機能しないことについて読んだことを漠然と覚えています(つまり、シングルタッチまたはピンチズームのいずれかを検出でき、それだけです)が、それをバックアップするための参照はありません...

Android 4 (Ice Cream Sandwich) で修正されたようです。そのため、Android 4 がまもなくリリースされるのを待ってから、もう一度試してみてください。それを超えて、Google は将来的に Android ブラウザを Chrome のモバイル バージョンに置き換えることを計画していると発表しました。

于 2011-12-20T14:01:03.557 に答える
1

これは理論です: 広範囲にテストできませんでした

webview のソース コードの init 関数によると、ステートメントsetFocusable(true)は常に初期化中に webview で呼び出されます。

エラーを使用してビューをフォーカス不可にしようとしたときにsetFocusable(false)、エラーは再び発生しませんでした。オレンジ色のボックスが表示されないようです。OS 2.3.4を実行している小さなサムスンの電話でこれをテストしていました。私が確信しているのは、このオレンジ色のボックスが再表示されなかったことです。

これが事実であることが判明した場合、独自の webview なしでこれを解決できる可能性が非常に高くなります。focusable プロパティを false に設定すると、他の問題が発生するため、事態がさら​​に複雑になります。

最後に、このプロパティを JavaScript から制御できるとは思いません (または制御できますか?)。たぶん、特定のコントロールまたはドキュメント全体が入力などではないことを宣言できますか? 私はこれが間違っているかもしれないほどむき出しに外挿しているだけです.

編集:あなたの質問に対するコメントに関して、 フォーカス可能なプロパティを false に設定した後に URL をロードする Webview のみを持つ空のアプリケーションを作成しました。テストするためのリソースが他にもある場合は、アップロードして試してみてください。お申し込みはこちら

于 2011-12-20T14:11:38.217 に答える
0

イベントを直接添付してみましたか?

$(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);
});

(コードが本当にずさんな場合は申し訳ありません。私はログ機能にあまり注意を払っていませんでしたが、私の touchend イベントで event.touches[0].pageX として正しく起動していなかったため、いくつかの小さな変更を加えました。はその時点では未定義です.また、怠け者だったので、準備ができている関数でラップしました:-P)

これは最初のタッチ (event.touches[0]) のみを追跡しているため、タッチ配列をたどってマルチタッチをテストするための調整を行うことができます。Android デバイス (ジンジャーブレッド) で発見したことは、2 本の指を同時に画面に置いた場合、最後のタッチが離されたときにのみ touchend イベントが発生するということです。つまり、2 番目の指を離します。

また、mousedown/mouseup イベント リスナーをアタッチすると、オレンジ色のハイライト全体で行ったのとまったく同じ結果が得られました。

私がテストしたデバイスは、OTA Gingerbread アップデートを備えた Samsung Droid Charge でした。

于 2011-12-23T00:43:19.787 に答える
0

jQuery Mobile イベントを使用してみましたか? 分離されたウィジェット/プラグインは次の場所にあります。

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

おそらく jquery.mobile.event.js と jquery.mobile.vmouse.js が必要になるでしょう

これで、jQuery 内のタップ、スワイプなどのイベントに簡単にバインドできます。それとも、タップの開始と終了を区別する必要がありますか?

于 2011-12-20T14:14:06.857 に答える
0

on を使用してすべてのイベントをアタッチする代わりに、これを試してください。

 $("...").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
 });
于 2011-12-21T04:22:32.710 に答える