ズーム機能を持つ描画UIでは、ズーム後にオブジェクトをクリックできない、オブジェクトの位置が変わった、といった問題が発生することがあります。
その原因の一つが、「viewport」を考慮できていないことです。
1. オブジェクトの座標と、見えているオブジェクトにズレが生じてしまう
画面上でオブジェクトを描画・操作できるキャンバス(描画領域)を実装している際の事例です。
描画したオブジェクトをズームした状態で操作しようとすると、クリックしている位置と、実際に判定されるオブジェクトの位置が一致せず、選択できなくなることがありました。
クリックイベントで取得した座標と、オブジェクトが保持している座標をそれぞれログに出して確認すると、一致していないことは分かりましたが、なぜズレが生じるのかが分かりませんでした。
計算ロジックを疑い、何度も見直したところ、原因は、viewportを考慮できていなかったことでした。
次の章では、そのviewportの考え方を整理していきます。
2. 座標は「3つの層」で整理できる(world / viewport / screen)

「viewport」とは、worldのどの範囲を、どの倍率で表示するかを定義する虫眼鏡のようなものです。
虫眼鏡を動かせば表示される範囲が変わり、倍率を変えれば拡大・縮小された状態で表示されます。
viewportは、worldの一部を切り出し、その結果をscreen上に描画します。
ズームやパンを行うと、「world上の座標が変わった」ように感じることがあります。
しかし実際には、worldの座標を書き換えているのではなく、viewportの拡大率や位置を変更しているだけの場合があります。
今回の実装でも、viewportを操作することでズームを実現していました。※パンは、画面を上下左右に移動させる操作のことです。
- worldはそのまま
- viewportが動く(ズーム・パン)
- screenの見え方が変わる
viewportがある最大の利点は、world(データ)をいじらずに、見え方だけを変えられることです。
もしviewportがなく、ズームやパンをworldの座標そのもので表現しようとすると、表示を変えるたびに「すべてのオブジェクト」を書き換える必要が出てきます。
- 座標(x, y)やサイズを更新し続ける
- 当たり判定も同じだけ更新する
- 表示を戻すときも、元の値に戻す必要がある
見せ方(表示範囲や倍率)を変えたいだけであれば、worldの座標を書き換える必要はありません。
ズームやパンで変えるのはviewportだけです。
3. viewportと変換の正体
viewportは、worldをどの範囲・倍率・位置で表示するかを定義する概念です。
これはアフィン変換[1]と呼ばれるもので、多くの環境では配列として管理されています。
[ scaleX, skewY, skewX, scaleY, translateX, translateY]
- scaleX / scaleY:
拡大・縮小(zoom) - translateX / translateY:
移動(pan) - skewX / skewY:
傾き(斜め方向の変形)
worldの座標そのものは変わりませんが、そこにこの配列の値を適用すると、screen上の座標が計算されます
2D:アフィン変換行列
Canvas / SVG / 地図 / エディタ など
3D:行列変換
ゲームエンジン / 3DCG / WebGL / AR / VR など
4. viewportを知った結果
ズームを行うと、screen上の座標はviewportの影響を受けて変化します。
しかし当初は、そのscreen座標をそのままworld座標と比較していました。
その結果、本来一致しているはずの位置でも、基準が異なるため選択判定が失敗していました。
クリック位置の座標を、viewportの拡大率や移動量を考慮してworld座標へ変換するように修正すると、選択判定は正しく機能するようになりました。
当初は「ズーム後に選択できない」という不具合の解消が目的でしたが、この整理により、
- worldは不変の座標空間として保持する
- 表示の拡大・移動はviewportで制御する
- 判定時は必ず座標の層を揃える
という構造が明確になりました。
さらに、この構造にしたことで、キャンバスのサイズそのものを意識する必要がなくなりました。
作業領域を広げたい場合も、詳細を確認したい場合も、worldのデータを変更せず、viewportを変更するだけで対応できます。
worldは変わらない。
変わっているのはviewportだけ。
viewportは単なるズーム機能ではなく、描画処理の役割を整理するための構造を提供します。
ここまでの説明は、CanvasやSVGのような2Dの描画環境を想定したものでした。
参考までになりますが、この座標変換の考え方自体は、より広い分野で使用されています。 ↩︎

