--- title: Pointer Events の使用 slug: Web/API/Pointer_events/Using_Pointer_Events tags: - Guide - Input - Pointer Events - PointerEvent - events - touch translation_of: Web/API/Pointer_events/Using_Pointer_Events ---
{{DefaultAPISidebar("Pointer Events")}}
このガイドでは、ポインタイベントと HTML の {{HTMLElement("canvas")}} 要素を使用してマルチタッチ対応の描画アプリを作成する方法について説明します。 この例は、{{domxref("PointerEvent","ポインタイベント", "", 1)}}の入力イベントモデルを使用する点を除いて、Touch events の概要の例に基づいています。 もう1つの違いは、ポインタイベントはポインタデバイスに依存しないため、アプリは同じコードを使用してマウス、ペン、または指先からの座標ベースの入力を受け入れることです。
このアプリは、ポインタイベントをサポートしているブラウザーでのみ動作します。
このアプリのライブ版は GitHub で利用できます。 ソースコードは Github で入手でき、プルリクエストやバグレポートは大歓迎です。
ブラウザーがデフォルトのタッチの振る舞いをアプリに適用しないようにするには、{{cssxref("touch-action")}} プロパティを none
に設定します。
<canvas id="canvas" width="600" height="600" style="border:solid black 1px; touch-action:none"> Your browser does not support canvas element. 訳: お使いのブラウザーはキャンバス要素をサポートしていません。 </canvas> <br> <button onclick="startup()">Initialize</button> <br> Log: <pre id="log" style="border: 1px solid #ccc;"></pre>
ページが読み込まれると、{{HTMLElement("body")}} 要素の onload
属性によって以下に示す startup()
関数が呼び出されます(ただし、MDN ライブサンプルシステムの制限により、この例ではボタンを使用してトリガーします)。
function startup() { var el = document.getElementsByTagName("canvas")[0]; el.addEventListener("pointerdown", handleStart, false); el.addEventListener("pointerup", handleEnd, false); el.addEventListener("pointercancel", handleCancel, false); el.addEventListener("pointermove", handleMove, false); log("initialized."); }
これは単に {{HTMLElement("canvas")}} 要素のすべてのイベントリスナーを設定するので、タッチイベントが発生したときに処理できます。
進行中のタッチを追跡します。
var ongoingTouches = new Array();
{{event("pointerdown")}} イベントが発生すると、表面上で新しいタッチが発生したことを示し、次の handleStart()
関数が呼び出されます。
function handleStart(evt) { log("pointerdown."); var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); log("pointerdown: id = " + evt.pointerId); ongoingTouches.push(copyTouch(evt)); var color = colorForTouch(evt); ctx.beginPath(); ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false); // a circle at the start 訳注: エラーで中断するので、この行を削除すべき ctx.arc(evt.clientX, evt.clientY, 4, 0, 2 * Math.PI, false); // a circle at the start ctx.fillStyle = color; ctx.fill(); }
イベントの処理の一部を後で処理するために ongoingTouches
に格納した後、開始点を小さな円として描画します。 4ピクセル幅の線を使用しているので、4ピクセルの半径の円が見栄えよく表示されます。
1つ以上のポインタが移動するたびに、{{event("pointermove")}} イベントが配信され、その結果、次の handleMove()
関数が呼び出されます。 この例におけるその役割は、キャッシュされたタッチ情報を更新し、各タッチの前の位置から現在の位置まで線を引くことです。
function handleMove(evt) { var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); var color = colorForTouch(evt); var idx = ongoingTouchIndexById(evt.pointerId); log("continuing touch: idx = " + idx); if (idx >= 0) { ctx.beginPath(); log("ctx.moveTo(" + ongoingTouches[idx].pageX + ", " + ongoingTouches[idx].pageY + ");"); ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY); log("ctx.lineTo(" + evt.clientX + ", " + evt.clientY + ");"); ctx.lineTo(evt.clientX, evt.clientY); ctx.lineWidth = 4; ctx.strokeStyle = color; ctx.stroke(); ongoingTouches.splice(idx, 1, copyTouch(evt)); // swap in the new touch record log("."); } else { log("can't figure out which touch to continue: idx = " + idx); } }
この関数はキャッシュされたタッチ情報配列で各タッチに関する以前の情報を探して、描画する各タッチの新しい線分の開始点を決定します。 これは各タッチの {{domxref("PointerEvent.pointerId")}} プロパティを見ることによって行われます。 このプロパティは、各ポインタイベントに対して一意の整数であり、各指が表面と接触している間、各イベントに対して一貫性を保ちます。
これにより、各タッチの前の位置の座標を取得し、適切なコンテキストメソッドを使用して2つの位置を結ぶ線分を描画できます。
線を描画した後、Array.splice()
を呼び出して、ongoingTouches
配列内のタッチポイントに関する以前の情報を現在の情報に置き換えます。
ユーザーが表面から指を離すと、{{event("pointerup")}} イベントが送信されます。 次の handleEnd()
関数を呼び出すことによってこのイベントを処理します。 その仕事は終わったタッチのための最後の線分を引き、進行中のタッチリストからタッチポイントを取り除くことです。
function handleEnd(evt) { log("pointerup"); var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); var color = colorForTouch(evt); var idx = ongoingTouchIndexById(evt.pointerId); if (idx >= 0) { ctx.lineWidth = 4; ctx.fillStyle = color; ctx.beginPath(); ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY); ctx.lineTo(evt.clientX, evt.clientY); ctx.fillRect(evt.clientX - 4, evt.clientY - 4, 8, 8); // and a square at the end ongoingTouches.splice(idx, 1); // remove it; we're done } else { log("can't figure out which touch to end"); } }
これは前の関数と非常によく似ています。 唯一の大きな違いは、終わりを示すために小さな正方形を描くことと、Array.splice()
を呼び出すときに、更新された情報を追加せずに、進行中のタッチリストから古いエントリを削除することです。 その結果、そのタッチポイントの追跡をやめます。
ユーザーの指がブラウザーの UI にぶつかったり、あるいはタッチをキャンセルする必要がある場合は、{{event("pointercancel")}} イベントが送信され、次の handleCancel()
関数を呼び出します。
function handleCancel(evt) { log("pointercancel: id = " + evt.pointerId); var idx = ongoingTouchIndexById(evt.pointerId); ongoingTouches.splice(idx, 1); // remove it; we're done }
アイデアはすぐにタッチを中止することなので、最後の線分を描かずに進行中のタッチリストから削除します。
この例では、コードの残りの部分をより明確にするために簡単に説明する必要がある、いくつかの便利な関数を使用しています。
各タッチの描画を異なるように見せるために、colorForTouch()
関数を使用して、タッチの一意の識別子に基づいて色を選びます。 この識別子は不透明な数字ですが、少なくとも現在アクティブなタッチ間で異なることを頼れます。
function colorForTouch(touch) { var r = touch.pointerId % 16; var g = Math.floor(touch.pointerId / 3) % 16; var b = Math.floor(touch.pointerId / 7) % 16; r = r.toString(16); // make it a hex digit g = g.toString(16); // make it a hex digit b = b.toString(16); // make it a hex digit var color = "#" + r + g + b; log("color for touch with identifier " + touch.pointerId + " = " + color); return color; }
この関数の結果は、描画色を設定するために {{HTMLElement("canvas")}} の関数を呼び出すときに使用できる文字列です。 例えば、{{domxref("PointerEvent.pointerId")}} の値が 10
の場合、結果の文字列は "#aaa"
です。
ブラウザーによっては、イベント間でタッチオブジェクトを再利用することがあるので、オブジェクト全体を参照するのではなく、気になる部分をコピーするのが最善です。
function copyTouch(touch) { return { identifier: touch.pointerId, pageX: touch.clientX, pageY: touch.clientY }; }
次の ongoingTouchIndexById()
関数は、ongoingTouches
配列をスキャンして、指定された識別子と一致するタッチを見つけ、そのタッチの配列におけるインデックスを返します。
function ongoingTouchIndexById(idToFind) { for (var i = 0; i < ongoingTouches.length; i++) { var id = ongoingTouches[i].identifier; if (id == idToFind) { return i; } } return -1; // not found }
function log(msg) { var p = document.getElementById('log'); p.innerHTML = msg + "\n" + p.innerHTML; }
仕様 | 状態 | コメント |
---|---|---|
{{SpecName('Pointer Events 2','#pointerevent-interface', 'PointerEvent')}} | {{Spec2('Pointer Events 2')}} | 不安定版 |
{{SpecName('Pointer Events', '#pointerevent-interface', 'PointerEvent')}} | {{Spec2('Pointer Events')}} | 初期定義 |
PointerEvent
インターフェイス{{Compat("api.PointerEvent", 0)}}