マウスとタッチの両方の操作をサポートするWebアプリケーションを作成しています。テストでは、Windows 7でタッチスクリーンデバイスを使用しています。最新のFirefoxおよびChromeカナリア)でタッチイベントをスニッフィングして、次の結果を得ました。
タッチすると、Firefoxはタッチと対応するマウスイベントを発生させます。 Chrome発火touchstart/mousedown
、touchend/mouseup
ペアですが、mousemove
は非常に奇妙な方法で起動しました:touchmove
の間に1/2回。
すべてのマウスイベントは通常どおり処理されます。
現代のタッチスクリーンでマウスとタッチイベントを同時に処理する方法はありますか? Firefoxがタッチとマウスのイベントのペアを起動すると、Chromeのtouchmove
でmousemove
がどうなりますか?すべてのマウスイベントをタッチまたはその逆に変換する必要がありますか?レスポンシブインターフェイスを作成する正しい方法を見つけたいと思います。
むしろ、タッチインターフェイスの可用性を確認し、それに応じてイベントをバインドする必要があります。
あなたはこのようなことをすることができます:
(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);
どのイベントをリッスンするかを事前に実際に予測することはできません(たとえば、ページが読み込まれた後にUSBタッチスクリーンが接続される可能性があることをすべて知っている場合)。
代わりに、常にタッチイベントとマウスイベントの両方をリッスンする必要がありますが、処理するタッチイベントでpreventDefault()を呼び出して、(現在は冗長な)マウスイベントが発生しないようにします。詳細は http://www.html5rocks.com/en/mobile/touchandmouse/ を参照してください。
MouseEventsとTouchEventsは技術的にまったく同じ機能を提供しませんが、ほとんどの目的で、それらは交換可能に使用できます。ユーザーがマウスとタッチスクリーンの両方を持っている可能性があるため、このソリューションはどちらか一方を優先しません。その代わり、ユーザーは、入力を変更する前に少なくとも5秒間待機する限り、ユーザーが希望する入力デバイスを使用できます。このソリューションは、画面がタップされたときにタッチスクリーンデバイスでのマウスポインターエミュレーションを無視します。
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.
{...}
これは決して勝つすべての解決策ではありません、私はこれを数回使用しましたが、問題は見つかりませんでしたが、公平を期すために、通常、キャンバスをタップしたときにアニメーションを開始するか、または回転するロジックを提供しますボタンにdiv。このコードを使用し、改善点を見つけ、このコードを改善するのは皆さんに任せます(より良い解決策が見つからない場合)。
私は同様のより複雑な問題があるため、このスレッドを見つけました:
矢印NEXT/PREVIOUSでjsが有効なスクロール可能な領域を作成するとします。これは、タッチイベントとマウスイベントに応答するだけでなく、ユーザーが画面を押し続けるかマウスを押し続ける間、繰り返しイベントを発生させる必要があります。
イベントを繰り返すと、次のボタンで1つではなく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
にも当てはまります。
入力デバイス(実際にはグラフィック環境全体)が順次イベントを生成することがわかっているため、プライベートな_eventLock.next.primary
_および_eventLock.previous.primary
_に特別なキーが設定されている限り、どちらが最初に起動するかは問題ではありません。ハンドラーnext*()
およびprevious*()
からそれぞれキャプチャされた最初のイベント。
そのkeyはイベントタイプであるため、2番目、3番目などのイベントは常に敗者であり、ロック関数eventLock()
およびprimaryEventLock()
。
(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)
の助けを借りてlockを取得せず、そこで停止するというものです。
このロックを開く唯一の方法は、ステップ(4)でterminationイベントハンドラー*End()
を呼び出すことです:previousEnd
およびnextEnd
。
セッションの途中で接続されたタッチデバイスの問題も非常にスマートな方法で処理します。_defaults.pressDelay
_よりも長く押し続けると、コールバック関数の連続呼び出しが生成されることに気付きましたonly forその時の主要なイベント(理由は、終了イベントハンドラーが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()
、つまりsetTimeout(previous, defaults.pressDelay)
には引数を渡すメカニズムがありません。
この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);