SYNCERのロゴ
アイキャッチ画像

ピンチイン・ピンチアウトでズームする方法

いわゆるズームです。タッチデバイスのピンチイン、ピンチアウトの動作を検出し、それによって画像を縮小、拡大してみましょう。難易度は高く、タッチした位置の取得方法など基本的な知識がある方を前提にしています。

サンプルコード

要点はピンチイン、ピンチアウトを開始した時と、指を動かした時の距離の比率を求めることです。

基本の距離

2本以上の指でピンチイン、ピンチアウトを開始した時の、2点間の距離を基本の距離(baseDistance)とします。touchstartイベントで距離を取りたいと思いがちですが、ここで重大な問題があります。2017年8月現在、Androidだとtouchstartイベントで1ヶ所のタッチしか受け取れません。だからtouchmoveイベントでまかないましょう。

JavaScript

// 基本の距離
var baseDistance = 0 ;

// 距離を測る関数
 function getDistance ( event ) {
	event.preventDefault() ;

	var touches = event.changedTouches ;

	// 2本以上の指の場合だけ処理
	if ( touches.length > 1 ) {
		// 座標1 (1本目の指)
		var x1 = touches[0].pageX ;
		var y1 = touches[0].pageY ;

		// 座標2 (2本目の指)
		var x2 = touches[1].pageX ;
		var y2 = touches[1].pageY ;

		// 2点間の距離
		return Math.sqrt( Math.pow( x2-x1, 2 ) + Math.pow( y2-y1, 2 ) ) ;
	}

	return 0 ;
}

// touchmove
element.ontouchmove = function ( event ) {
	// 操作開始時の距離
	baseDistance = getDistance( event ) ;
}

変化後の距離

次に開始位置から2本指を動かした時、その距離を変化後の距離(movedDistance)とします。先ほどのtouchmoveイベント内を変更します。

element.ontouchmove = function ( event ) {
	if ( baseDistance ) {
		// 変化後の距離
		var movedDistance = getDistance( event ) ;
	} else {
		// 基本の距離
		baseDistance = getDistance( event ) ;
	}
}

比率の計算

基本の距離と変化後の距離の比率を算出します。当たり前ですが、ピンチインで距離が縮めば比率は1以下、ピンチアウトで距離が拡がれば比率は1以上になりますよね。この比率を、そのまま画像の横幅、高さに適用すれば縮小、拡大できるというわけです。

var scale = movedDistance / baseDistance ;

デモ

前章で説明した要点を踏襲して作成したコードです。タッチデバイスを使って、枠内の黄色い部分でピンチイン、ピンチアウトをしてみて下さい。それに合わせて画像が縮小、拡大するはずです。ピンチイン、ピンチアウトの操作が少しでも止まったら、基本の距離を更新するようにしましょう。

// <div id="target1"></div>
var divElement = document.getElementById( "target1" ) ;

// <img id="target2">
var imgElement = document.getElementById( "target2" ) ;

// base
var beseDistance = 0 ;
var baseImageWidth = 0 ;
var baseImageHeight = 0 ;

// timeout id
var timeoutId ;

// touchmove
divElement.ontouchmove = function ( event ) {
	event.preventDefault() ;

	var touches = event.changedTouches ;

	if ( touches.length > 1 ) {
		var x1 = touches[0].pageX ;
		var y1 = touches[0].pageY ;

		var x2 = touches[1].pageX ;
		var y2 = touches[1].pageY ;

		var distance = Math.sqrt( Math.pow( x2-x1, 2 ) + Math.pow( y2-y1, 2 ) ) ;

		clearTimeout( timeoutId ) ;

		if ( beseDistance && baseImageWidth && baseImageHeight ) {
			var scale = distance / beseDistance ;

			if ( scale && scale != Infinity ) {
				imgElement.width = baseImageWidth * scale ;
				imgElement.height = baseImageHeight * scale ;
			}

			timeoutId = setTimeout( function () {
				beseDistance = 0 ;
				baseImageWidth = 0 ;
				baseImageHeight = 0 ;
			}, 100 ) ;

		} else {
			beseDistance = distance ;
			baseImageWidth = imgElement.width ;
			baseImageHeight = imgElement.height ;

		}
	}
}
  • Twitterでシェア
  • Facebookでシェア
  • Google+でシェア
  • はてなブックマークでシェア
  • pocketに保存
  • LINEでシェア
更新履歴
2017年7月31日 (月)
コンテンツを公開しました。