JavaScript:スマホでタッチした座標を取得する

JS:JavaScriptでスワイプ処理をする JavaScript

スマホ、タブレット、あるいはタッチ式のPCでは、HTMLエレメント(div, p, canvas等)に対してタッチイベントが発生します。
JavaScriptを使って特定タグの枠内でタッチしたxy座標を取得するサンプルを紹介します。

パソコンのブラウザでタッチ操作をチェックしたい場合は、以下を参考にしてください。

TouchEventの種類

タッチイベントの種類は、touchstart, touchend, touchmoveの3つです。
これはマウスイベントでいうmousedown, mouseup, mousemoveに近いものです。

touchstartタッチしたとき(指が触れたとき)
touchendタッチを終了したとき(指が離れたとき)
touchmoveタッチしたまま操作しているとき(指を触れたまま動かしたとき)
※上記では、指と説明していますが、スタイラスペン等での操作も同様です

タッチイベントには、touchcancelというイベントもありますが、ここでは割愛します。詳しくはMDNのドキュメントを参照してください。

参考

TouchEvent - Web API | MDN
TouchEvent インターフェイスは、タッチ感応面への接触状態が変化したときに発生する UIEvent を表します。...

タッチイベントでxy座標を取得する大まかな説明

touchstartイベント

HTMLエレメントをelementとした場合、以下のコードでタッチ開始時touchstartイベント)のxy座標を取得できます。

element.addEventListener("touchstart", function(e){
    // 端末のデフォルト動作をキャンセル
    e.preventDefault();

    // タッチ開始位置を取得
    const x = e.touches[0].pageX;

    const y = e.touches[0].pageY;
    // 処理
    //   :
});

スマートフォンなどの端末ではタッチ操作が端末特有の操作イベントに対応している場合が多い為、処理の最初に

e.preventDefault();

を実行して端末特有の操作イベントを無効化する必要があります。

e.touchesは可変長の配列で、タッチしている指の数に応じて配列要素数が変化します。
複数のタッチに対応していることから、e.touchesは、タッチした順にe.touches[0], e.touches[1], e.touches[2]… となっていきます。

プロパティpageX, pageYは、読み取り専用で、ページ左上からのHTMLエレメント上の座標を返します。

上記プログラムで、実際に返される変数x, yの値は、

x = 188.33334350585938
y = 248.33334350585938

などと実数値をとるため、利用する際は Math.floor(e.touches[0].pageX) などと整数化することが多いかもしれません。

touchendとtouchmoveイベント

touchendtouchmoveでは、e.touchesではなくe.changedTouchesを使ってxy座標を取得します。
その他は、touchstartと同じです。

touchmoveでの例

element.addEventListener("touchmove", function(e){
    // 端末のデフォルト動作をキャンセル
    e.preventDefault();

    // タッチ中の位置を取得
    const x = e.changedTouches[0].pageX;

    const y = e.changedTouches[0].pageY;
    // 処理
    //   :
});

【重要】タッチ座標の起点を考慮しないとならない場合がある

もし、タッチイベント対象のHTMLエレメントがページの左上に位置していれば問題ないのですが、他のHTML要素の上下などに配置されていたりするとpageX, pageYで取得されるxy座標はページ左上を(x,y) = (0, 0)の起点とした座標となります。

これはイメージで見た方が分かりやすいです。

例)HTML内にcanvasタグを配置したときのイメージ

もし、上記の例でcanvasの左上座標を(0, 0)としたい場合は、canvasのタッチイベントで取得したpageXとpageYから、実際のページ左上からのオフセット値x=50, y=100をそれぞれ引いてあげる必要があります。

このオフセット値は、対象エレメントのgetBoundingClientRect()left, topプロパティがそれぞれ対応します。
実際には以下のようなイメージでcanvasの左上を(0, 0)とすることができます。

element.addEventListener("touchstart", function(e){
    // 端末のデフォルト動作をキャンセル
    e.preventDefault();

    // ページ上のオフセット位置取得
    const offset = this.getBoundingClientRect();

    // タッチ開始位置を取得
    const x = e.touches[0].pageX - offset.left;

    const y = e.touches[0].pageY - offset.top;
    // 処理
    //   :
});

参考

Element: getBoundingClientRect() メソッド - Web API | MDN
Element.getBoundingClientRect() メソッドは、要素の寸法と、そのビューポートに対する相対位...

サンプルのコード

サンプルコードでは、オフセット値を考慮し、全てgetBoundingClientRect()を使って、HTMLエレメントの左上を起点(0, 0)とした座標を返すようにしています。

touchstartを使ったサンプル

touchstartは、タッチした時にイベントが発生します。
枠内のタッチしたxy座標を返します。
複数タッチに対応して表示させるためにe.touchesの要素数分for文を使ってループさせています。iPhoneでは5本指まで表示できました。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="main.js"></script>
    <title>タッチイベント: touchstart</title>
</head>
<body>
    <h1>touchstart</h1>
    <main></main>
</body>
</html>

style.css

/* style.css */
@charset "utf-8";

*{
    margin: 0;
    padding: 0;
}

main{
    border: 1px solid #555;
    width: 300px;
    height: 300px;
}

main.js

// main.js

// 起動時の処理
window.addEventListener("load", ()=>{
    const main = document.querySelector("main");

    main.addEventListener("touchstart", function(e){
        // 端末のデフォルト動作をキャンセル
        e.preventDefault();

        // タッチ開始情報を取得
        const touches = e.touches;

        // ページ上のオフセット位置取得
        const offset = this.getBoundingClientRect();

        let touchValue = "";
        for(let i=0; i<touches.length; i++){
            const x = Math.floor(touches[i].pageX - offset.left);   // x座標
            const y = Math.floor(touches[i].pageY - offset.top);    // y座標
            touchValue += `touches[${i}] x: ${x}, y: ${y}<br>`;
        }

        this.innerHTML = touchValue;
    });
});

touchendのサンプル

基本的にはtouchstartと同じですが、touchend指を離したときにイベントが発生します。
また、座標の取得はe.touchesではなくe.changedTouchesを利用します。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="main.js"></script>
    <title>タッチイベント: touchend</title>
</head>
<body>
    <h1>touchend</h1>
    <main></main>
</body>
</html>

style.css

/* style.css */
@charset "utf-8";

*{
    margin: 0;
    padding: 0;
}

main{
    border: 1px solid #555;
    width: 300px;
    height: 300px;
}

main.js

// main.js

// 起動時の処理
window.addEventListener("load", ()=>{
    const main = document.querySelector("main");

    main.addEventListener("touchend", function(e){
        // 端末のデフォルト動作をキャンセル
        e.preventDefault();

        // タッチ終了情報を取得
        const touches = e.changedTouches;

        // ページ上のオフセット位置取得
        const offset = this.getBoundingClientRect();

        let touchValue = "";
        for(let i=0; i<touches.length; i++){
            const x = Math.floor(touches[i].pageX - offset.left);   // x座標
            const y = Math.floor(touches[i].pageY - offset.top);    // y座標
            touchValue += `touches[${i}] x: ${x}, y: ${y}<br>`;
        }

        this.innerHTML = touchValue;
    });
});

touchmoveのサンプル

touchmoveタッチした指を動かしているときにイベントが発生します。
座標の取得はe.changedTouchesを利用します。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="main.js"></script>
    <title>タッチイベント: touchmove</title>
</head>
<body>
    <h1>touchmove</h1>
    <main></main>
</body>
</html>

style.css

/* style.css */
@charset "utf-8";

*{
    margin: 0;
    padding: 0;
}

main{
    border: 1px solid #555;
    width: 300px;
    height: 300px;
}

main.js

// main.js

// 起動時の処理
window.addEventListener("load", ()=>{
    const main = document.querySelector("main");

    main.addEventListener("touchmove", function(e){
        // 端末のデフォルト動作をキャンセル
        e.preventDefault();

        // タッチ移動中情報を取得
        const touches = e.changedTouches;

        // ページ上のオフセット位置取得
        const offset = this.getBoundingClientRect();

        // タッチ移動中の座標を全て表示
        let touchValue = "";
        for(let i=0; i<touches.length; i++){
            const x = Math.floor(touches[i].pageX - offset.left);   // x座標
            const y = Math.floor(touches[i].pageY - offset.top);    // y座標
            touchValue += `touches[${i}] x: ${x}, y: ${y}<br>`;
        }

        this.innerHTML = touchValue;
    });
});

コメント

タイトルとURLをコピーしました