to topto bottom

モバイルファースト時代のマルチデバイス対応を確実・簡単に

Webサイトでスマホ・タブレットのスワイプ動作(touchmove)イベントを確実に取得するには

スワイプ動作

こんにちは。x-fit開発チームの高島です。
 
スマートフォンやタブレットでは、画面の様々な動作をイベントとして取得することができます。スマートデバイス向けのWebサイトでは、スワイプ動作(画面に触れたまま滑らせる動作)で画面を制御することもよく行われます。
スワイプ動作は、JavaScriptのtouchmoveイベントで取得できますが、注意しないとiOSとAndroidで異なる動きをすることがありますので、試してみました。

【HTML&Javascriptコード】

下記のようなコード(抜粋)を使って、canvas上でスワイプした場所にグレーの円を描画してみます。touchmoveイベントは、スワイプしている間、連続的に発生します。発生するすべてのイベントを描画処理するとパフォーマンスが悪くなるため、描画処理は定期的に行うようにして、その時点での接触場所に描画するようにします。
 
 
<meta charset="UTF-8">
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>touchmove event</title>
<style type="text/css">
body {
  margin: 0px;
  overflow: hidden;
}
canvas {
  border: solid black 1px;
}
</style>
<script type="text/javascript">
  var canvas;
  var ctx;
  var w = 0;
  var h = 0;

  var timer;
  var drawStarted = false;
  var touches = [];

  function init() {
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');

    //15ミリ秒ごとに描画処理します。
    timer = setInterval(draw, 15);

    //canvasの見た目の大きさと描画サイズを合わせます。
    canvas.style.width = '300px';
    canvas.style.height = '300px';
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    w = canvas.width;
    h = canvas.offsetHeight;

    canvas.addEventListener('touchend', function() {
      ctx.clearRect(0, 0, w, h);
    });

    canvas.addEventListener('touchmove', function(event) {
      //マルチタッチの場合、タッチ箇所がリストで取得されます。
      touches = event.touches;
    });
  };

  function draw() {
    //他の描画処理が行われているときは描画しません。
    if (drawStarted)
      return;
    drawStarted = true;
    ctx.clearRect(0, 0, w, h);

    var i, len = touches.length;
    for (i = 0; i < len; i++) {
      var touch = touches[i];
      var px = touch.pageX;
      var py = touch.pageY;

      ctx.beginPath();
      ctx.arc(px, py, 40, 0, 2 * Math.PI, true);
      ctx.fillStyle = "rgba(100, 100, 100, 0.5)";
      ctx.fill();
    }
    drawStarted = false;
  };
</script>

<body onload="init()">
  <canvas id="canvas"></canvas>
</body>
 
 

【確認結果】

上記のコードを、iOS(iPhone4S)・Android(Galaxy Nexus)それぞれで表示すると、以下のような現象が発生しました。
  • iOSでは、イベントは連続的に発生。スワイプ方向が横方向であるときは、スワイプ個所に追随するように、円が連続的に描画される。ただ、縦方向のスワイプでは、ブラウザ画面全体のスクロールが優先され、円の描画は行われなかった。

  • Androidでは、イベントは連続的に発生しない。イベントは、スワイプ時に1回だけ発生し、円は、離れた場所(最初に指を置いた地点)に1回だけ描画された。

 
この現象は、スワイプ動作とそれに対するそれぞれのOSのデフォルト動作があるため、サイト上のスワイプ動作と競合してしまうために発生しているようです。
 
 

【対応】

Webサイト上でスワイプ動作イベントを正しく取得するためには、もともとのスワイプ動作を抑止する必要があります。JavaScriptでは、EventにpreventDefaultメソッドがありますのでそれを利用します。touchstartイベント発生時に、そのイベントに対してpreventDefaultをコールします。上記のコードに修正するようであれば、下記のようなコードを加えるイメージです。これで、iOS、Androidともに、どの方向へのスワイプ動作でも連続的にイベントを取得できるようになります。
 
canvas.addEventListener('touchstart', function(event) {
   event.preventDefault();
});
 
※本記事は、弊社開発チーム所有機での確認結果であり、全機種調査の結果ではありません。同一機種であっても、この現象が必ず発生することを保証するものではありません。これ以外や例外のある可能性がありますことをご了承ください。
 
タグ: 
このエントリーをはてなブックマークに追加