2015

8

8

HTML5とJavaScriptでPC・スマホのマルチタッチ対応してやんよ!!!

スポンサードリンク


レスポンシブデザインのようにサイトの仕様をPC・スマホ(タブレット)の両方に対応させるフレキシブルなページを設計するためには、各デバイスのイベントプロパティに詳しくなりましょう!

PCとスマホ共通のイベント、スマホ特有のイベント

PCをマウスで操作する場合と、スマートフォンを画面にタッチして操作する場合で、全く異なる処理を記述しなければならないわけではありません。
マウスで操作するためのイベントの多くが、スマホ・タブレットでも利用できますが、一部スマートフォンがサポートしていないマウス関連のイベントもあります。

マウスイベントとスマートフォンでの操作の対応関係

マウスイベント PCでの操作 スマホでの操作
onmousedown マウスボタンから指を離した 指が画面から離れた
onmouseup 押しているマウスボタンから指を離した 指が画面から離れた
onmousemove マウスカーソルが移動している なし
onmouseover マウスカーソルがオブジェクトの上に載った なし
onmouseout マウスカーソルがオブジェクトから離れた なし
onclick マウスでクリックした 指で画面をタップした

スマホ・タブレット(タッチ端末)特有のイベント

タッチイベント スマホ・タブレットでの操作
ontouchstart 画面に指が触れた
ontouchmove 画面に指を触れたまま動かした
ontouchend 画面から指を離した
ontouchcancel ontouchendの発生前に処理が取り消された
ongesturestart ジェスチャーの開始(※iOSのみ)
ongesturechange ジェスチャーの変更(※iOSのみ)
ongestureend ジェスチャーの終了(※iOSのみ)

イベント発生時の処理を記述する

イベントに対応する処却を記述する方法は二つあります。
一つはイベントハンドラを利用する方法、もう一つは、イベントリスナーを処理する方法です。

画面に指が触れたときのイベント

dom.ontouchstart = function(event) {
    // タッチイベントの処理を記述
};
domの部分には、ボタンなどのDOM (Document Object Modeil)要素を指定します。

イベントリスナーを利用する場合

dom.addEventListener("touchstart", function(event) {
    // タッチイベントの処理を記述
}, false);
第l引数にイベント名を、第2引数にイベント発生時に実行する処理を記述します。
イベント名は、イベントからonを取ったものです。例えば、「ontouchstart」であれば、
「touchstart」がイベント名になります。


スマホ画面スクロールを無効にする

dom.ontouchmove = function(event) {
    // 画面のスクロールを防止する
    event.preventDefault();
    // タッチイベントの処理をここに記述
};
preventDefaultメソッドは、イベントがキャンセル可能である場合、イベントの伝播を止めずに、そのイベントをキャンセルします。
タッチイベントにおいては、ブラウザのデフォルトイベントである、画面のスクロール操作をキャンセルします。

画面に触れている複数の指の座標を取得

複数の指でスマホ画面にタッチすると、それぞれの指の座標を表示するプログラムです
<!DOCTYPE html><html><head>
<meta charset="UTF-8">
<title>タッチイベント検出</title>
<meta name="viewport" content="width=device-width">
<style> canvas{ border: solid 1px red; } </style>
</head>
<body>
<canvas id="ac" width="300" height="300"></canvas>
<div id="disp"></div>
<script>
var ac = document.getElementById("ac"); // canvas要素のオブジェクトを取得
ac.ontouchstart = function (e) {
	e.preventDefault();		// デフォルトイベントをキャンセル
	var s = "";				// 変数sを初期化
	
	// 引数のtouchesプロパティは配列の要素数(触れている指の数)だけ繰り返し処理
	for (var i = 0; i < e.touches.length; i++) {
		var t = e.touches[i];		// 触れている指に関する情報を取得
	 
		s += "[" + i + "]";
		s += "x=" + t.pageX + ",";
		s += "y=" + t.pageY + "<br>";
	}
	document.getElementById("disp").innerHTML = s;	// 生成した文字列を画面に表示
};
</script>
</body></html>

処理の概要

  • ●canvas要素のDOMオブジェクトを取得し、このオブジェクトに対してontouchstartイベントハンドラを定義します。
  • ●タッチイベントが発生すると、このイベントハンドラで定義した処理が実行されます。
  • ●イベントハンドラ関数の引数としてタッチ関連の情報が保持されたオブジェクトが渡されます。
  • ●関数内ではまず、e.preventDefaultメソッドを実行し、指が触れたときにブラウザ画面がスクロールするのを防ぎます
  • ●指でタッチした座標の情報を取得し、画面に表示する文字列を作成します
  • ●引数のオブジェクトのtouchesプロパティは配列になっており、触れているそれぞれの指の情報が、各要素に格納されています。
  • ●そこで、touchesプロパティ配列の要素数、すなわち画面に触れている指の数だけ、処理を繰り返して、座標を表す文字列をつないでいきます


フリックを検出する

スマホ画面を横にフリック移動を検出するプログラムです
<!DOCTYPE html><html><head>
<meta charset="UTF-8"><title>フリック検出</title>
<meta name="viewport" content="width=device-width">
<style>
#page {border: 5px solid blue;}
.pad { width:100px; height: 100px;}
</style>
</head>
<body>
<div id="page">
<div class="pad">
</div></div>
<script>

//フリックと見なすしきい値。開始位置のX座標フリック判定用フラグ
var flick_limit = 100;
var flick_sx = 0;
var flick_flag = false;

$("#page").style.position = "absolute";
$("#page").style.width = document.width + "px";
$("#page").style.height = document.height + "px";

// 画面に指が触れたときの処理を定義
document.addEventListener("touchstart", function(e) {
	//スクロール無効化										 
	e.preventDefault();
	// 指が触れた位置のx座標を記録
	flick_sx = e.touches[0].pageX;
	// フリック判定用フラグ(false)
	flick_flag = false;
});

// 画面上で指を移動させているきの処理を定義
document.addEventListener("touchmove", function(e) {
	// スクロール無効化										 
	e.preventDefault();
	// 指が触れた位置のx座標を記録
	var x = e.touches[0].pageX;
	// 指の移動距離(X軸方向のみ)を算出
	var v = Math.abs(x - flick_sx);
	
	// 移動距離がしきい値を超えていたらフリック判定を切り替え
	flick_flag = (v > flick_limit);
	
	// フリック中のアニメーション+スタイル
	if(v > flick_limit) {
		$("#page").style.left = (x - flick_sx) + "px";
		$("#page").style.background = "rgba(255, 255, 0, 1)";
	}
});

// 画面から指が離れたときの処理を定義
document.addEventListener("touchend", function(e) {
	e.preventDefault();
	
	// フリック判定用フラグがtrueならばアラート表示
	if(flick_flag) {
		alert("フリックされました");
	}
	// フリック判定用フラグを切り替え(false)
	flick_flag = false;
	
	// フリック中のアニメーション+スタイルを初期化
	$("#page").style.left = "0px";
	$("#page").style.background = "rgba(255, 255, 0, 0)";
});

// 引数で指定した要素(id)のオブジェクトを取得
function $(id) {
	return document.querySelector(id);
}
</script></body></html>

処理の概要

  • ●フリックと見なすしきい値、開始位向のX出標、フリック判定用フラグを的納するグローパル変数をそれぞれ定義します
  • ●アニメーションをわわせるページのcssプロパティを設定しておきます
  • ●画面に指が触れたことを示すtouchstartのイベントリスナーでは、横方向のフリックを検出するために、指が画面に触れた位置のX座標をグローパル変数に記録しておきます
  • ●画面上を指が移動していることを示すtouchmoveのイベントリスナーで、画面上を指が
    移動した距離を算出し、移動距離がしきい値を超えていたらフリック判定則フラグをtrue(真)に設定します
  • ●画面から指が離れたことを示すtouchendのイベントリスナーで、フリック判定用フラ
    グがtrueの場合、アラー卜表示を実行します
  • ●最後にページの横方向に移動した絶対位置と装飾プロパティを元の値に戻します


ピンチイン、ピンチアウトの実装

viewportのデフォルト設定では拡大・縮小をさせないフレームワーク仕様も、ページによっては可能にできるように対応
指1本のフリックで画像が移動し、指2本のピンチイン・ピンチアウトで、画像を拡大/縮小します。
<!DOCTYPE html><html><head>
<meta charset="UTF-8"><title>フリック検出</title>
<meta name="viewport" content="width=device-width, user-scalable=no">
<style>
* { padding:0; margin:0; }
#photo {
	margin-top: 50px;
}
</style>
</head><body>
<div id="disp"></div>
<div><img id="photo" src="http://tokidoki-web.com/wp-content/themes/yanyo/images/logo.png"></div>
<script>

$("#photo").style.position= "absolute";
// 各変数の宣言
var pinch_dist, base_x = 0, base_y = 0;
var photo_x = 0, photo_y = 0, photo_r = 1;


// 画面に指が触れたときの処理を定義
document.addEventListener("touchstart", function(e) {
	//スクロール無効化										 
	e.preventDefault();
	// 指1本のときは触れているXY座標を記録
	if (e.touches.length <= 1) {
		base_x = e.touches[0].pageX;
		base_y = e.touches[0].pageY;
	// 指2本の2本の指のX座標の差とY座標の差を加えた値を記録	
	} else if (e.touches.length >= 2) {
		var p1 = e.touches[0];
		var p2 = e.touches[1];
		pinch_dist = Math.abs(p1.pageX - p2.pageX) + Math.abs(p1.pageY - p2.pageY);
	}
});


// 画面上で指を移動させているきの処理を定義
document.addEventListener("touchmove", function(e) {
	// スクロール無効化										 
	e.preventDefault();
	
	var p1 = e.touches[0];
	// 指1本の移動距離に応じて画像の左上の座標を算出
	if (e.touches.length <= 1) {
		photo_x += p1.pageX - base_x;
		photo_y += p1.pageY - base_y;
		base_x = p1.pageX;
		base_y = p1.pageY;
	// 指2本の指の間の距離に応じて拡大・縮小率を算出	
	} else if(e.touches.length <= 2) {
		var p2 = e.touches[1];
		var dist = Math.abs(p1.pageX - p2.pageX) + Math.abs(p1.pageY - p2.pageY);
		photo_r = dist / pinch_dist;
	}

});

// タイマーの処理。15ミリ秒ごとに画像を更新
setInterval(function(){
	// 画像のオブジェクトを取得
	var style = $("#photo").style;
	style.left = photo_x + "px";
	style.top = photo_y + "px";
	// 画像サイズを変更
	var scale = "scale(" + photo_r + "," + photo_r + ")";
	style.transform = scale;
	style.webkitTransform = scale;
	// デバッグ情報を表示
	$("#disp").innerHTML = scale;
}, 15);


// 引数で指定した要素(id)のオブジェクトを取得
function $(id) {
	return document.querySelector(id);
}
</script></body></html>

処理の概要

  • ●touchstartイベントでは、画面に触れている指がl本の場合にはその座標を記録
  • ●指2本の場合には2点聞の”距離”を計算して記録します
    ※プロセサへの負荷を軽減するために計算を簡略化して、2本の指の「X座標の差」と「Y座標の差」の和を疑似的に距離と見なしています。
  • ●touchmoveイベントで、指1本の場合は指の移動距離に応じた画像の新しい座標を算出します
  • ●指2本の場合は2点聞の距離の算出し、X・Y座標の値との比を取って画像の拡大・縮小率を計算します
    ※touchmoveイベントは指を動かすたびに多量に発生します。
    そのため、画像の描両などの重い処理は行わず、移動後の座標あるいは拡大・縮小率の計算だけを行い、実際の写真の移動や拡大・縮小の処理は、定期的に実行するタイマーイベントの中で行っています
  • ●タイマーイベントの中で画像のpositionプロパティを「absolute」にしてleft/ topプロパティで左上座標の位情.を指定することで移動させtransformプロパティに「scale(X拡大率・Y拡大率)」を指定することで拡大・縮小します


トップへ