以游標為中心縮放的反推公式
滾輪縮放時要讓「游標下方那個世界座標點,縮放後仍對到同一個螢幕位置」,否則畫面會飛走。
做法
const worldBefore = screenToWorld(cursor, viewport) // 縮放前游標下的世界座標
const nextScale = viewport.scale * zoomFactor
const offsetX = cursor.x - worldBefore.x * nextScale
const offsetY = cursor.y - worldBefore.y * nextScale原理
想要:worldToScreen(worldBefore, newViewport) === cursor
把 worldToScreen 公式 screen = world * scale + offset 解出 offset:
offset = cursor - worldBefore * nextScale
換句話說:「先用舊 viewport 把游標位置反推成世界座標 → 再用新 scale 算出要把畫布平移多少,才能讓那個世界點仍貼著游標」。
為什麼直覺做法是錯的
如果只改 scale 不調 offset,畫布等於以左上角 (0, 0) 為中心縮放,使用者覺得內容「飛走了」。Figma、Miro、tldraw 全部以游標為中心,這是必備 UX。