系列:一張投影片背後的工程:從零打造網頁簡報編輯器
對應程式版本:Git tag
v0.4.0版本說明:本文記錄第一版的扁平群組模型,不建立額外的群組容器物件。
把標題、圖示和說明文字一起移動,看似只要一次把多個座標加上相同位移;真正容易出錯的是選取、預覽、提交與複製必須看成同一組。第一版用最小的資料結構處理:每個物件保留自己的 frame,只多一個可選的 groupId。
群組時,reducer 對被選取 ID 寫入同一個新群組 ID:
updateDocument({
type: 'element/set-group',
slideId: resolvedActiveSlideId,
elementIds: selectedElementIds,
groupId: makeId('group'),
})
文字、圖片和圖形仍各自保存 x、y、寬高與樣式,因此渲染器不必辨識新的巢狀節點。點到群組內任一物件時,選取邏輯會找出同一個 groupId 的全部物件;取消群組則收集目前投影片的整個群組後,刪除這個欄位。
拖曳開始時,movingElements 取得目前選取的群組成員。移動中的畫面只保存一個共同 delta,每個成員都套用同樣的預覽位移;放開後才送出一次批次 action:
updateDocument({
type: 'element/translate',
slideId: resolvedActiveSlideId,
elementIds,
x: delta.x,
y: delta.y,
})
一次提交讓復原紀錄、自動儲存與畫布外界檢查都看見一個完整操作,而不是群組內每個元素各留一筆歷史。群組拖曳時的吸附也以整組的外框計算,因此不會讓其中一個成員越界。
Reducer 測試先把文字與矩形設為 group-1,再以 (20, 10) 批次平移。文字從 (180, 170) 變成 (200, 180),矩形也同步位移。接著把兩者置頂,物件陣列維持 text-1 在 shape-1 前方,證明群組不會破壞相對順序。
這個模型的限制也很明確:一個物件只有一個 groupId,目前沒有巢狀群組、群組級 frame 或群組級樣式。它已足以支援一起選取、移動、複製與取消群組;需要階層式設計時再增加真正的群組節點。