iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
Software Development

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

Day 04|建立第一張可以呈現內容的投影片

  • 分享至 

  • xImage
  •  

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

對應程式版本:Git tag day-04

版本說明:本文記錄當日實作;後續版本增加的物件與欄位不改變這裡的共用渲染方式。

上一篇定義了簡報、投影片和物件的資料結構。資料模型能保存內容,卻還不會自己變成畫面。這一篇先完成最短的渲染路徑:讀取目前投影片,依序把文字與圖片放到固定尺寸的畫布上。

先讓資料直接對應畫面

第一版的渲染器 SlideRenderer 只接收兩項必要資料:一張 Slide 和文件的 canvas 尺寸。畫布背景來自投影片,每個物件則依照自己的型別選擇文字或圖片元件。以下省略選取、編輯與拖曳所需的 props,只保留渲染分流:

<SlideRenderer
  slide={activeSlide}
  canvas={presentation.canvas}
>
  {activeSlide.elements.map((element) =>
    element.type === 'text'
      ? <TextElementView key={element.id} element={element} />
      : <ImageElementView key={element.id} element={element} />
  )}
</SlideRenderer>

React 的 key 使用物件本身的 id,而不是陣列索引。新增、刪除或重新排序後,同一個物件仍能保有穩定身分,React 不會因為位置改變而誤用另一個元件的狀態。

用邏輯座標擺放物件

渲染器本身是 position: relative,文字和圖片物件使用 position: absolute。因此模型中的 xywidthheight 可以直接成為 CSS:

const frameStyle = {
  left: element.x,
  top: element.y,
  width: element.width,
  height: element.height,
}

絕對定位元素會以最近的定位祖先作為包含區塊,所以 left: 120px 代表距離投影片左側 120 個邏輯單位,而不是距離整個瀏覽器視窗。所有資料都留在固定的 1600 × 900 座標系統中;畫面縮放只影響外層呈現,不回寫物件資料。

這個做法也讓順序很清楚:陣列後面的物件會畫在前面的物件之上。未來加入圖層調整時,只需要改變 elements 的順序,不必另外建立一套座標規則。

文字先使用瀏覽器原生輸入元件

文字物件由 TextElementView 顯示。平常 textarea 是唯讀,雙擊後才進入編輯狀態;輸入值改變時,再送出明確的文件更新 action。這讓選取物件與編輯文字維持兩種不同狀態,也能直接使用瀏覽器既有的游標、選字和中文輸入法行為。

文字大小、顏色和對齊方式仍來自文件:

style={{
  fontSize: element.style.fontSize,
  color: element.style.color,
  textAlign: element.style.textAlign,
}}

圖片物件則保存 assetId,由 AssetImage 到 IndexedDB 取得 Blob 並建立可顯示的 URL。渲染器不用知道圖片實際存放方式,只負責把取得的內容放進指定矩形。

編輯、縮圖與列印共用渲染器

同一張投影片不只出現在中央畫布,也會出現在左側縮圖與列印頁面。如果三處各寫一套 JSX,背景、字型或圖片尺寸很容易逐漸不一致。

SlideRenderer 因此提供兩種用法:編輯區傳入可互動的 children;縮圖與列印不傳 children,改由渲染器產生靜態元素。兩者仍共用畫布尺寸、背景、物件框和資產讀取。空白投影片的提示只在互動情境顯示,不會印進 PDF。

day-04 的測試涵蓋中文文字輸入、圖片匯入、IndexedDB 還原,以及每張投影片各自產生列印頁面。當時 6 個測試檔、24 個案例全部通過;Edge 手動驗收也確認投影片內容能正確出現在編輯區、縮圖和列印預覽。

到這裡,資料模型已能透過同一個渲染器出現在編輯畫布、縮圖與列印頁面。下一篇會比較 DOM/CSS 與 Canvas,並說明為什麼第一版選擇現在這套畫布做法。

參考資料


上一篇
Day 03|一張投影片需要哪些資料
下一篇
Day 05|選擇畫布的呈現方式
系列文
從零打造網頁簡報編輯器13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言