系列:一張投影片背後的工程:從零打造網頁簡報編輯器
對應程式版本:Git tag
v0.4.0版本說明:本文描述第一版以未旋轉矩形外框計算的吸附規則。
自由拖曳讓物件容易靠近,卻很難剛好對齊。吸附功能不能只比較游標位置:使用者看的是物件的左、中、右與上、中、下是否對齊,群組移動時還必須把整組當成一個外框。
constrainAndSnapMove 先算出選取集合的外框,將原始位移限制在 1600 × 900 邏輯畫布內。接著建立兩組目標:畫布的左、中、右與上、中、下,以及其他物件的對應三個錨點。
const xTargets = [0, canvas.width / 2, canvas.width].concat(
otherElements.flatMap((element) => [
element.x,
element.x + element.width / 2,
element.x + element.width,
]),
)
移動外框也取左、中、右三個值,逐一比較與所有目標的差距。只有差距不超過 10 個邏輯單位,才採用絕對值最小的 offset。X、Y 分開吸附;套用 offset 後再限制一次,確保吸附不會把物件推出畫布。
函式回傳兩種結果:真正要提交的 delta,以及可能的 guides。畫面把 guides.x、guides.y 畫成垂直或水平線;放開或取消拖曳時,兩者都清空。參考線不寫進文件,重新整理後不會留下過期的輔助線。

拖曳物件接近畫布中心時,編輯器顯示水平與垂直的暫時吸附參考線。
純函式測試有一個具體例子:寬度 100、起點 X 為 100 的物件收到 (295, -95) 位移時,結果被修正成 (300, -100),並回傳 { x: 500, y: 0 }。它剛好對齊另一個物件的 X=500 與畫布頂端。
拖曳事件在進入吸附函式前,已先除以目前畫布比例。因此 50%、100%、125% 都會得到相同的邏輯座標與對齊結果。第一版的 10 單位門檻同樣是邏輯單位,所以畫面上的可吸附距離會隨縮放改變:50% 約為 5 個螢幕像素,125% 約為 12.5 個像素。這是刻意保留的簡化;若未來希望手感固定,可把目標螢幕像素門檻除以 scale。
目前規則只使用未旋轉的外框。旋轉後的視覺邊界需要另一套幾何計算,不能假裝仍然完全準確。