iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
Build on Google AI

《30天打造喵語日誌:Gemini API × Vibe Coding 實戰》系列 第 10

Day 10:【多媒體元件】前端照片上傳與預覽模組實作

  • 分享至 

  • xImage
  •  

從文字陪伴走向「視覺陪伴」

在前幾篇文章中,我們完成了《喵語日誌》的文字對話、情緒分析和溫暖 UI。

不過,在真實的生活紀錄裡,一張照片往往比一大段文字更能傳達當下的感受:可能是今天吃的精緻串燒、路上看到的夕陽,或是貓咪賣萌的瞬間。

今天,我們來替《喵語日誌》解鎖照片上傳功能。

手機隨手拍下的照片,通常都是高解析度大檔(動不動就 3MB~10MB)。如果直接將原始大圖轉成 Base64 傳送給 API,會遇到網路傳輸慢、Payload 過大導致 API 失敗,以及前端介面卡頓的問題。

因此,今天的目標是在前端完成:圖片格式與大小檢查Canvas 等比例壓縮即時預覽拖曳上傳體驗

核心技術:使用 Canvas 壓縮圖片

為了在使用者選擇照片的瞬間自動完成「瘦身」,我們建立了一個 compressImage 工具函式。

其主要流程包含:
檢查檔案格式與大小 ➔ 用 FileReader 讀取 ➔ 用 Canvas 等比例縮放 ➔ 輸出壓縮後的 Base64 字串

1. 建立圖片壓縮工具 (src/utils/imageUtils.ts)

// Canvas 繪製與壓縮核心邏輯
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;

const ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0, width, height);

// 輸出壓縮後的 Base64 字串 (JPEG 格式,品質 0.7)
const compressedBase64 = canvas.toDataURL("image/jpeg", quality);

2. 壓縮效果實測

我用一張實際照片測試:

  • 原始大小:1437.0 KB(約 1.4 MB)
  • 壓縮後:149.0 KB(約 0.14 MB)
  • 體積縮減:超過 89%!

壓縮後的圖片在聊天氣泡中依然清晰,但傳輸量大幅下降。

整合進 ChatContainer:點擊上傳與拖曳體驗

接下來,把圖片上傳功能整合到 ChatContainer.tsx,主要包含三個互動細節:

  1. 點擊上傳:使用隱藏的 input (type=file),並透過相機 Icon 按鈕觸發點擊。
  2. 拖曳上傳:監聽 dragoverdrop 事件,並呼叫 preventDefault() 避免瀏覽器直接打開圖片分頁;拖曳覆蓋層加上 pointer-events-none 樣式,防止阻擋拖放事件。
  3. 預覽與刪除:選擇圖片後,在輸入框上方顯示縮圖,並提供右上角的 X 按鈕供使用者撤回。
// 拖曳覆蓋層與預覽縮圖 UI 邏輯
{isDragging && (
  <div className="pointer-events-none absolute inset-0 flex items-center justify-center bg-pink-50/80 border-2 border-dashed border-pink-300 rounded-3xl">
    <p className="text-pink-600 font-medium">放開以放置照片 🐾</p>
  </div>
)}

{/* 縮圖預覽與刪除按鈕 */}
<div className="relative mb-2 inline-block px-4">
  <img className="h-16 w-16 rounded-xl object-cover border border-pink-200" alt="預覽" />
  <button className="absolute -right-1 -top-2 rounded-full bg-stone-700 text-white p-1 text-xs">✕</button>
</div>

實測成果與壓縮效能分析

1. 圖片預覽與刪除縮圖

點擊相機圖示選取圖片後,輸入框上方會出現帶有移除按鈕的預覽縮圖。
https://ithelp.ithome.com.tw/upload/images/20260811/20178708p1qvZcfM2i.png

2. 拖曳上傳體驗

直接將電腦中的照片拖曳進對話視窗時,畫面上會出現粉紅色虛線覆蓋層,提示「放開以放置照片 🐾」。
https://ithelp.ithome.com.tw/upload/images/20260811/20178708LBYayJdCGL.png

3. 訊息發送與 Console 壓縮數據

按下送出後,聊天氣泡成功渲染出壓縮後的照片與喵喵的回應。同時開啟 DevTools 控制台觀察壓縮數據:

  • 原始圖片大小:1437.0 KB
  • Canvas 壓縮後體積:149.0 KB
  • 體積縮減幅度:超過 89%

https://ithelp.ithome.com.tw/upload/images/20260811/20178708XTXR4hKI25.png

壓縮後的圖片在聊天氣泡中依然清晰,但傳輸量大幅下降,對前端效能和使用者體驗都是很大的幫助。

心得與結語

這次實作讓我深刻體會到,優秀的 UI/UX 不僅在於看得見的動畫與配色,隱藏在背後的資料效能優化同樣關鍵。從格式與大小的防呆檢查、Canvas 等比例壓縮、點擊與拖曳上傳,到即時的預覽縮圖,在前端做好這些細節,不僅讓系統機能更穩健,也讓使用者上傳照片時體驗到零焦慮、無卡頓的流暢感。

現在的《喵語日誌》已經能同時處理文字與照片,讓使用者不只能跟喵喵說話,也能把生活中的美食或景致畫面直接分享給喵喵看。

明天我們將進入全新的資料視覺化單元!我們將引入 JavaScript 圖表庫 Chart.js,學習如何將使用者每天紀錄的心情數據,轉化為清晰美麗的週與月情緒折線圖,讓大家能一眼看懂自己這段時間的情緒起伏與轉變!喵~


上一篇
Day 09:【UI/UX 優化】打造溫暖風格的色彩計畫與動態對話氣泡
下一篇
Day 11:【圖表模組】情緒歷程視覺化:用 Chart.js 畫出情緒起伏月曆
系列文
《30天打造喵語日誌:Gemini API × Vibe Coding 實戰》17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言