zoom.on - ズームイベントの内容を設定する

投稿日: / 更新日:

d3.jsのzoom.onは、ズーム操作に対応したイベントリスナーを設定するためのメソッドです。イベントリスナーが受け取る情報に応じて、要素を動かすための処理を設定しないといけません。

サンプルコード

<div>
	<img src="/image.png">
</div>
// ズーム操作のコンストラクタを作成
var zoom = d3.behavior.zoom() ;

// ズーム操作に対応するイベントリスナーを指定する
zoom.on( "zoom", function() {
	// イベントオブジェクト
	var event = d3.event ;

	// スケール (現在の倍率)
	var scale = event.scale ;

	// トランスレート (X方向、Y方向への移動距離)
	var tx = event.translate[0] ;
	var ty = event.translate[1] ;
} ) ;

// div要素に対して、call()メソッドでズーム操作を適用
d3.select( "div" ).call( zoom ) ;

デモ

下のボタンでイベントを設定後に、画像をズームさせる動作をして取得できる値を確認して下さい。ホイール操作、ダブルクリック、ピンチイン、ピンチアウトに対応しています。このデモでは、ズーム動作に伴い情報を取得するだけで、画像は実際にはズームされません。

構文

zoom = zoom.on( type [, listener ] )

引数

項目説明
typeイベントハンドラを指定する。zoomstart(ズームの開始)、zoomend(ズームの終了)、zoom(ズーム操作中)の3種類から指定可。
listener省略可。設定したいイベントの関数を指定する。nullを指定した場合、既に設定されているイベントが削除される。第2引数以降を省略した場合、要素に設定されているイベントを取得する。

戻り値

項目説明
zoomコンストラクタが戻り値となる。

チュートリアル

zoom.onを利用してイベントリスナーを設定したとしても、その時点では、ズーム操作に応じた情報を取得できるようになっただけに過ぎません。ここからは、ダブルクリックやホイール操作、ピンチインやピンチアウトによって、イベントリスナーに送られてくる情報を利用して、対象の要素を拡大、縮小する方法を説明します。

コンストラクタの作成

まずは、おさらいです。要素にズーム操作を適用しましょう。例えば画像を拡大、縮小したい場合、ズーム操作を適用するのは親要素にしましょう。画像に適用すると、計算の元となる中心座標を変更しながらの計算になり、動作が安定しません。

<div id="parent">
	<img src="/image.png" id="target">
</div>

百聞は一見にしかずということで、例えば画像にズーム操作を適用した時の完成バージョンを確認してみて下さいね。ドラッグ操作で位置を移動する時に、カクカクになってしまいます。

// ズーム操作のコンストラクタを作成
var zoom = d3.behavior.zoom() ;

// img要素にドラッグ操作を適用する (非推奨)
d3.select( "#target" ).call( zoom ) ;

このコードを確認する

親のdiv要素にズーム操作を設定することで、動作が安定します。

// ズーム操作のコンストラクタを作成
var zoom = d3.behavior.zoom() ;

// div要素にズーム操作を適用する
d3.select( "#parent" ).call( zoom ) ;

このコードを確認する

ズームイベントの設定

要素にズーム操作を適用したら、ズーム操作によって受け取る情報を元にした処理をしなければいけません。そのための関数(イベントリスナー)を設定していきましょう。3種類のイベントハンドラが用意されています。

イベントハンドラの種類
項目説明
zoomstartズーム操作の開始時に発火する。
zoomズーム操作中に繰り返し発火する。
zoomendズーム操作の終了時に発火する。

zoomstartとzoomend

zoomstartと、zoomendは、ズームの開始時と終了時にそれぞれ1回だけ発火するイベントです。ズームを開始する時にちょっとだけ画像を縮小したい、終了した時にちょっとだけ膨らませて戻したいなど、そのタイミングでかけたい処理を設定しましょう。ここでは、どの操作がzoomstartzoomendの対象になるのか分かりやすいように、背景色を変えてみましょう。開始時に赤、終了時に茶色に戻ります。

// 開始時のイベント
zoom.on( "zoomstart", function() {
	// this=要素 (<div id="parent">...</div>)

	// 画像の背景を赤にする
	d3.select( "#target" ).style( "background", "red" ) ;
} ) ;

// 終了時のイベント
zoom.on( "zoomend", function() {
	// this=要素 (<div id="parent">...</div>)

	// 画像の背景を茶色にする
	d3.select( "#target" ).style( "background", "#D36015" ) ;
} ) ;

このコードを確認する

zoom

いよいよ、肝となるzoom(ズーム操作)のイベントリスナーを設定しましょう。このハンドラだけ、関数内のイベントオブジェクト(d3.event)に下記の2つのプロパティが含まれます。受け取ったこの情報と同じ数値を、そのまま要素のtansformプロパティに設定してあげればいいだけです。初見では複雑な印象を受けますが、やってることと言えば、受け取った値を横流しをするだけ。とっても簡単なんです。

参照できる情報
項目説明
scale元の大きさに対する倍率。transformscaleにあたる。
translate第1要素にX方向、第2要素にY方向への移動距離を含んだ配列。それぞれ、transformtranslateXtranslateYにあたる。
// ズーム操作中のイベント
zoom.on( "zoom", function() {
	// イベントオブジェクト
	var event = d3.event ;

	// スケール (現在の倍率)
	var scale = event.scale ;

	// トランスレート (X方向、Y方向への移動距離)
	var tx = event.translate[0] ;
	var ty = event.translate[1] ;

	// transformのtranslateX、translateY、scaleの3つをそれぞれ更新する
	d3.select( "#target" ).style("transform", "translateX( " + tx + "px ) translateY( " + ty + "px ) scale( " + scale + " )" ) ;
} ) ;

このコードを確認する

スタイルシートの注意点

ここで注意しなければいけないのは、ズームの対象となる要素(この章ではimg要素)のスタイルです。まず、transformの計算の起点を、左上に設定しておきます。こうすることで、ダブルクリック、マウスホイールにおけるズーム時に、マウス位置に向かってズームされてくるようになります。これを指定しないと、少し気持ち悪い挙動になっちゃいます。

#target {
	transform-origin: 0 0 ;
}

また、画像の位置をtext-alignmarginによって変更していると、それが計算に影響して、意図しない動作になってしまうのでご注意下さい。

#parent {
	/* ダメ */
	/* text-align: center ; */
}

#target {
	transform-origin: 0 0 ;
	margin: 0 ;
}

非推奨の場合を確認する

画像の初期位置の調整

ただ、画像を常に一番左上に置けというのでは芸がありません。画像の位置を初期状態で変更しておきましょう。text-alignmarginではなく、初期状態のtranslateを指定します。例えば、下記の通りです。ただ、これだけでは、実際の画像のtranslateの値(X方向に150px)と、コンストラクタ内部で管理しているtranslateの値(X方向に0px)が一致しません。ズレているとどうなるかを確認してみて下さいね。

// 画像を本来の位置よりX方向に150px移動させておく
#target {
	transform: translateX( 150px ) ;
}

この時点でのコードを確認する

解決するために、translate()を利用して調整しましょう。translate()は、ズーム操作のコンストラクタ内部で管理している、translateの値を変更できるメソッドです。実際の画像の位置と、コンストラクタ内部で管理する値を一致させることによって画像の初期位置を調整することができました。これは、translateだけでなくscaleにも応用できます。

// コンストラクタをzoomという変数に代入している場合

// translateのX方向の値を150に設定する
zoom.translate( [ 150, 0 ] ) ;

このコードを確認する