地図アプリ操作のように、マウスカーソル位置を中心に画像を拡大・縮小したり、ドラッグでスクロールさせたりする方法
リポジトリ
https://github.com/yasuhto/ZoomableImage
拡大・縮小
マウスカーソル位置を中心に拡縮させるということは、カーソル位置の絵は動きません。つまり、拡縮サイズに応じて画像本体の中心位置(transform.position)を調整します。
ググったところ、mousepancyo さんがほぼ目的のコードを公開されていたので、こちらを参考にしました。
主な変更箇所は2か所です。
- LocalScaleの更新タイミングをオフセット座標計算後に変更
- 縮小時の画像がCanvas内に収まるよう調整
LocalScaleの更新タイミングをオフセット座標計算後に変更
参照元のコードを手元で動作確認したところ、拡縮時に拡縮したいポイントとマウスカーソル位置がズレる症状が確認できました。原因は、中心位置のオフセットを計算する際に使用した RectTransformUtility.ScreenPointToLocalPointInRectangle に渡す RectTransform が変更後のスケールになっていたためです。
LocalScaleの更新タイミングをオフセット座標計算後に変更します。
// ズームするスケールを計算
var scale = this.transform.localScale * (1 + val * this.ZoomSpeed);
// カーソルを中心にズームするようにオフセット座標を計算
var offsetPos = new Vector3(this._CursorPotision().x * (scale.x - pastScale.x), this._CursorPotision().y * (scale.y - pastScale.y), 0f);
var pos = pastPos - offsetPos;
this.transform.localScale = scale;
this.transform.localPosition = pos;
private Vector2 _CursorPotision()
{
var canvas = this.GetComponentInParent<Canvas>();
var imRect = this.GetComponent<RectTransform>();
RectTransformUtility.ScreenPointToLocalPointInRectangle(imRect, Input.mousePosition, canvas.worldCamera, out var pos);
return pos;
}
縮小時の画像がCanvas内に収まるよう調整
「ある点を中心に拡大 → 別の点を中心に拡大 → 縮小」して元のサイズに戻すと、初期の画像の中心座標に戻らず、Canvasから画像がはみ出してしまいます。
画像の中心座標を調整する際にCanvasサイズを超えないように調整します。
public static Vector3 CalcAdjustedImageLocalPosition(Vector3 inPos, Vector3 scale, float width, float height)
{
var posXMax = width * 0.5f * (scale.x - 1);
var posYMax = height * 0.5f * (scale.y - 1);
var outPos = Vector3.zero;
outPos.x = inPos.x > 0 ? Mathf.Min(inPos.x, posXMax) : Mathf.Max(inPos.x, -1 * posXMax);
outPos.y = inPos.y > 0 ? Mathf.Min(inPos.y, posYMax) : Mathf.Max(inPos.y, -1 * posYMax);
return outPos;
}
スクロール
マウスのドラッグ距離に応じて画像の中心座標を移動させることで、スクロールを実現しています。ここでも、拡縮時と同じように画像がCanvas内に収まるように調整します。
var dragDelta = this.CalcMouseDragDelta();
var pos = this.transform.localPosition + dragDelta;
var scale = this.transform.localScale;
// 画像がCanvas内に収まるよう調整
var imRect = this.GetComponent<RectTransform>();
pos = CalcAdjustedImageLocalPosition(pos, scale, imRect.rect.width, imRect.rect.height);
this.transform.localPosition = pos;