首頁 > 軟體

JS圖形編輯器場景座標視口座標的相互轉換

2023-01-18 14:01:57

圖形編輯器座標系

圖形編輯器的座標系有兩種。

一個是場景(scene)座標系,一個是 視口(viewport)座標系。視口就是場景的一個子區域。

假設我們的視口的原點,離場景原點的座標水平和垂直距離分別為 scrollX 和 scrollY。

先 不考慮縮放,假設我們在視口座標上的某個地方點選了一下,這個座標是 (x, y)。這個座標在場景座標系中,就是:

const sceneX = scrollX + x;
const sceneY = scrollY + y;

挺簡單。

視口座標轉換為場景座標

下面我們引入畫布縮放,即畫布可以縮小和放大,對應的一個比例值 zoom。

視口中的某個座標 (x, y) 在場景座標系,則是 :

function viewportCoordsToSceneCoords(x, y, scrollX, scrollY, zoom) {
  return {
  x: scrollX + x / zoom,
  y: scrollY + y / zoom
  }
}

之所以要用 x 除以 zoom,是因為此時視口中展示的是縮放後的圖形,裡面的座標都是縮放後的值。所以需要先轉換為 zoom 值為 1 對應的真實值。

場景座標轉換為視口座標

然後我們反過來,如何從場景座標 (x, y) 轉換為視口座標?將前面的公式做等式變換即可:

function sceneCoordsToViewportCoords(x, y, scrollX, scrollY, zoom) {
  return {
  x: (x - scrollX) * zoom,
  y: (y - scrollY) * zoom
  };
}

我們通常是使用按鍵加滾輪的方式讓畫布以遊標為中心進行縮放,或按按鈕進行縮放,

為了讓縮放後的場景還能對上縮放前遊標的位置,我們需要計算縮放後的 scrollX 和 scrollY,進行校準。

核心思路是 保持縮放前點到視口左上角距離(視口座標系)相同

function calScrollVal(cx, cy, prevZoom, zoom, scrollX, scrollY) {
  // 先計算目標點的場景座標(這裡 cx 和 cy 是基於視口座標系的)
  const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(cx, cy, prevZoom, scrollX, scrollY);
  // 縮放後畫布縮放比變成了 zoom,距離視口左上角的距離變成了 cx / zoom
  // 減去這個距離,就是新的 scrollX 了。
  const newScrollX = sceneX - cx / zoom;
  const newScrollY = sceneY - cy / zoom;
  return {
    x: newScrollX,
    y: newScrollY
  };
}

再說點別的。

可能會有這麼一種情況,就是實際的視口區域的原點座標有一些偏移,偏移了 offsetX 和 offsetY,見下圖。

我們只需要將前面程式碼中的 scrollX 變成 (scrollX + offsetX),scrollY 變成 (scrollY + offsetY),其他不變。

就這些了。

總結一下,視口座標是場景座標平移並縮放後的結果,所以視口轉場景,需要除以 zoom 再加上偏移值。在圖形編輯器中,會有相當多的座標系轉換邏輯,這兩個座標系的關係需要好好消化理解,更多關於JS場景視口座標轉換的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com