系列:一張投影片背後的工程:從零打造網頁簡報編輯器
對應程式版本:Git tag
v0.4.0版本說明:本文記錄第一版的編輯器內剪貼簿;它可跨投影片使用,但不等同於作業系統剪貼簿。
複製物件的難點不是再放一份相同資料,而是避免 ID 重複、避免新副本錯連到舊群組,也不要把同一張圖片的二進位資料複製很多次。第一版先把範圍控制在同一個編輯器工作階段內,用 useRef 保存內部剪貼簿。
按下 Ctrl/Cmd+C 時,程式把目前選取集合做深層複製:
const copySelection = () => {
clipboardRef.current = structuredClone(selectedElements)
}
剪下先複製,再以 element/delete-many 刪除原物件。貼上時不直接加入剪貼簿內容,而是走 duplicateElements:每個元素用 crypto.randomUUID() 產生新 ID,座標增加 24 個邏輯單位後再限制在畫布內。
文字與圖形的 style 會建立新物件;圖片保留既有 assetId,所以新副本仍引用同一份 IndexedDB 資產,而不是再存一份 Blob。
若複製集合中有群組,不能沿用舊 groupId,否則新舊物件會被當成同一組。程式用 Map 為每個來源群組建立一個新 ID:同一來源群組的副本仍會聚在一起,不同來源群組則不會混在一起。
貼上後,內部剪貼簿更新為這一批新副本。連續貼上會每次再偏移 24 單位,使用者能看見新物件而不會完全重疊。
全域快捷鍵支援 Ctrl/Cmd+C、X、V、D,且在文字輸入、組字或表單欄位焦點中不攔截原生行為。介面測試先框選文字與箭頭,剪下後確認原投影片沒有物件;新增投影片再貼上,確認新頁出現兩個物件。
這裡的「跨投影片」只代表同一個瀏覽器頁面、同一次編輯工作階段。重新整理、關閉分頁或貼到其他應用程式不會保留這個內部剪貼簿;若要支援系統剪貼簿,還需要處理瀏覽器權限與可攜資料格式。