iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
Software Development

從零打造網頁簡報編輯器系列 第 20 篇

Day 20|不重建物件的圖片替換與裁切

  • 分享至 

  • xImage
  •  

系列:一張投影片背後的工程:從零打造網頁簡報編輯器

對應程式版本:Git tag v0.4.0

版本說明:本文記錄圖片 frame、圖片資產與裁切設定彼此分離的第一版,並補充後續加入的未使用圖片清理。

使用者替換圖片時,通常想保留已排好的位置和大小。若先刪除舊物件再建立新物件,ID、群組、圖層與 frame 都可能跟著消失。這一版只更換圖片來源,不刪掉原本的投影片物件。

frame 與圖片來源分開保存

ImageElement 的 x、y、寬高來自共用 frame;圖片本身只用 assetId 指向 IndexedDB 裡的 Blob,另存替代文字、顯示方式與裁切位置。

替換時會先驗證 PNG/JPEG 與 20 MB 上限,再把新檔案存成新的資產。成功後只送出新的 assetId 與檔名(當作替代文字),reducer 把它們合併進原本的物件:

if (element.type === 'image' && action.type === 'element/set-image') {
  const image = { ...element, ...action.image }
  // 比較圖片欄位後回傳 image
}

React state 仍會建立不可變的新值,但元素的 ID、frame、群組與圖層位置都保留下來,顯示方式與裁切位置也沿用原設定。新圖片的比例不同時,frame 仍維持原本的寬高,多出來的空間交給下一節的 contain/cover 處理。若資產寫入失敗,文件也不會先指向不存在的圖片。

裁切交給瀏覽器處理

圖片框只提供兩種顯示方式:contain 完整顯示,cover 填滿後裁切。裁切位置可選中央、上、右、下或左,最後直接對應到原生 CSS:

style={{
  objectFit: fit ?? 'contain',
  objectPosition: position ?? 'center',
}}

位置在 cover 時決定保留圖片的哪一側,在 contain 時則決定留白出現在哪一邊,兩種模式都能調整。這個做法不會修改原始 Blob,也不必另外產生裁切後的圖片。AssetImage 從 IndexedDB 取得 Blob 後建立暫時的 object URL,元件卸載或資產改變時再撤銷 URL。

驗證替換後仍能保存

介面測試先匯入 640 × 480 的 chart.png,確認文件與 Blob 都已保存;接著替換成 replacement.png,確認編輯畫布、縮圖與列印頁面三處都改顯示新圖片;重新讀取保存的文件後,圖片物件也已換成新檔名。

替換後不會立即刪除舊的資產,這其實是必要的:按下復原會把物件的 assetId 換回舊值,若舊 Blob 已經刪除,復原後只會看到「圖片無法載入」。備份則只收集文件仍在使用的圖片,不會把舊圖一起帶走。後來的修正(commit 4bd1dbd)把清理放在開啟編輯器時:這時復原紀錄還是空的,文件就是唯一的引用來源,可以安全刪除沒用到的圖片;若 Web Locks API 顯示其他分頁也開著編輯器就先略過,因為那邊的復原紀錄可能還需要它們。

參考資料


上一篇
Day 19|用同一套渲染器加入圖形與箭頭
下一篇
Day 21|復原重做如何記住一次完整操作
系列文
從零打造網頁簡報編輯器 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言