任意の点を中心に、画像や動画を拡大・縮小する方法

以前、任意の点(マウスカーソルの位置)を中心に拡大・縮小する方法を goo blog に投稿したことがありますが、全くもって不十分な内容であったため、書き直すことにします。

また、これまではコード例によくJavaを使っていましたが、今回はLiveデモが見せやすいJavaScriptを使うことにします。

1. 画像左上の点を起点とした拡大・縮小

まずは、画像の左上の点 (0, 0) を起点とした単純な拡大・縮小を実装します。

今回は imageScale という現在の倍率を表す変数を用意し、
マウスホイールの操作によって 0.2 ずつ増減するものとします(最小1.0~最大5.0)。

画像が変数 image に格納されているとき、
画像を imageScale 倍して HTML の Canvas に貼り付ける処理は、
例えば以下のように実装できます。

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 画像のスケーリング表示
ctx.save();
ctx.scale(imageScale, imageScale);
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
ctx.restore();

5行目で save() によって現在の Canvas の状態を保存しています。
6行目で以後の描画命令を imageScale 倍し、7行目で image(imageScale倍)を Canvas全体に描画します。
8行目で描画状態を元に戻しています。

コード全体の例は以下のとおりです。
画像上でマウスホイール操作を行うと、左上を起点とした画像の拡大・縮小が行われます。

2. 任意の点を起点とした拡大・縮小

実際には、マウスの位置を中心とした拡大・縮小をしたいケースが多いかと思います。
はじめ私は、「1. 画像左上の点を起点とした拡大・縮小」のコードを拡張して実装を行っていましたが、
JavaScript には画像の特定領域を描画する drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) 関数があり、
これを使えば良さそうでした(Java等にも同様のメソッドが用意されています)。

上記 drawImage() では、画像上の点 (sx, sy) から幅 sw, 高さ sh の矩形領域を切り抜き、
Canvas上の点 (dx, dy) から幅 dw, 高さ dh の矩形領域に描画を行ないます。

切り抜くべき領域は元画像の 1 / imageScale 倍のサイズであるので、
sw = canvas.width / imageScale, sh = canvas.height / imageScale となります。

また、切り抜いた矩形領域を描画する先は Canvas全体であれば良いので、
dx = 0, dy = 0, dw = canvas.width, dh = canvas.height となります。

あとはマウスの点に応じて切り抜きの開始点 (sx, sy) を求めるだけですが、これが少々厄介です。
マウス座標だけでなく、直前の拡大・縮小操作によってなされた画像座標の移動も考慮する必要があるためです。
切り抜き領域の開始点を (zoomLeft, zoomTop), 幅を zoomWidth, 高さを zoomHeight,
マウス座標を (mouseX, mouseY) とすると、拡大操作時の各変数は次のように計算できます。

// 切り抜き領域の幅(sw), 高さ(sh) の計算
zoomWidth = canvas.width / imageScale;
zoomHeight = canvas.height / imageScale;

// 切り抜き領域の開始点X座標 (sx) の計算
zoomLeft += mouseX * SCALE_STEP / (imageScale * (imageScale - SCALE_STEP));
zoomLeft = Math.max(0, Math.min(canvas.width - zoomWidth, zoomLeft));

// 切り抜き領域の開始点Y座標 (sy) の計算
zoomTop += mouseY * SCALE_STEP / (imageScale * (imageScale - SCALE_STEP));
zoomTop = Math.max(0, Math.min(canvas.height - zoomHeight, zoomTop));

ここで SCALE_STEP は一回の拡大操作で増減する拡大率のステップ値であり、本記事では 0.2 としています。
6行目では、マウス座標に拡大ステップを乗じて、直前の拡大率と現在の拡大率を乗じた値で除し、自身に加算しています。
なぜこの計算で切り抜き領域の開始点座標が計算できるのか詳しい説明は省きますが、
マウスが画面左端にあったとき、中央にあったとき、右端にあったとき、
それぞれどのような計算結果にしたいかを考えていけば、導くことができます。
7行目は状況によっては不要・冗長になるかもしれませんが、
切り抜きの開始点が 0 よりも小さかったり、canvas.width – zoomWidth よりも大きいということは
決してあってはならないので、その範囲内に収まるようなエラー対策をしています。

高さ方向および縮小時についても同様のコードが書け、
これらをまとめると以下のように実装することができます。

3. ドラッグ操作による描画領域の移動

最後はおまけのようなものですが、拡大・縮小操作に加えて
マウスドラッグによる画像の移動処理の例を紹介します。

特に複雑なコードは必要なく、マウス押下時に座標を記録し、ドラッグ距離を計算、
距離に応じて zoomLeft, zoomTop の値を更新するだけです。
ただし Canvas からはみ出ないよう、zoomLeft, zoomTop の最小・最大値は制御する必要があります。
実際のコード例は以下のとおりです。

コメント

  1. 匿名 より:

    コピペ出来るようにしては?

  2. 匿名 より:

    ありがとうございます!

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