iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Modern Web

微光採集瓶:用 Vue 3 打造每日美好記錄網頁系列 第 22 篇

【Day 22】儲存美好回憶:微光色卡展開檢視與圖片下載

  • 分享至 

  • xImage
  •  

實作目標

在 Day 21 完成「時光相簿」的資料封存與清單陳列後,Day 22 的核心目標有兩個:

  1. 點擊相簿中的微縮色卡展開大圖檢視:解決目前卡片縮太小、看不清細節的問題。
  2. 色卡匯出為圖片下載(雙入口設計):透過 html2canvas 套件,在「當月色卡彈窗」與「相簿大圖彈窗」都提供一鍵下載 PNG 圖檔的功能。

html2canvas 是什麼?

html2canvas 是一個可以將網頁上的 HTML / DOM 元素轉換成 Canvas 圖像的 JavaScript 套件。這次月卡本身是由 HTML、CSS 與漸層效果組成,因此可以利用它把畫面上的色卡「截取」成 Canvas,再進一步轉換成 PNG 圖檔,讓使用者可以將月卡保存到自己的裝置。

一、安裝截圖套件

在終端機執行安裝指令,安裝常用的 DOM 轉圖片套件:

npm install html2canvas

https://ithelp.ithome.com.tw/upload/images/20261006/20178067vwJEU4s6PF.png

有顯示出最上面那行就代表安裝成功了喔!

二、引入套件並宣告狀態與方法

打開 src/components/GemJar.vue,在 <script setup> 區塊進行以下擴充:

1. 引入 html2canvas

在最上方加入引入:

import html2canvas from 'html2canvas'

2. 宣告相簿「展開大圖」的選中狀態

在 showAlbum 變數附近加入:

// 目前在相簿中被點選、要展開大圖檢視的色卡
const selectedAlbumCard = ref(null)

// 打開相簿卡片大圖
function openCardDetail(card) {
  selectedAlbumCard.value = card
}

// 關閉相簿卡片大圖
function closeCardDetail() {
  selectedAlbumCard.value = null
}

// 綁定卡片 DOM 的 ref 與匯出狀態
const currentCardRef = ref(null)
const detailCardRef = ref(null)
const isExporting = ref(false)

3. 實作通用下載函式(匯出為 PNG)

使用 html2canvas 抓取 DOM 繪製成 Canvas。這裡特別加入 onclone 與白色底色設定,確保截圖時能排除外層動畫與陰影干擾,還原最純粹鮮明的極光色彩:

async function exportCardAsImage(elementRef, filenamePrefix = 'mood-card') {
  if (!elementRef || isExporting.value) return
  
  try {
    isExporting.value = true
    
    // 將 DOM 節點繪製為高解析度 Canvas
    const canvas = await html2canvas(elementRef, {
      scale: 2, // 解析度清晰
      useCORS: true,
      backgroundColor: '#ffffff',
      onclone: (clonedDoc, clonedElement) => {
        // 排除動畫、變形與陰影運算對 Canvas 像素的干擾
        clonedElement.style.animation = 'none'
        clonedElement.style.transform = 'none'
        clonedElement.style.boxShadow = 'none'

        const blurGlass = clonedElement.querySelector('.aurora-blur-glass')
        if (blurGlass) blurGlass.style.display = 'none'

        const innerGlow = clonedElement.querySelector('.aurora-inner-glow')
        if (innerGlow) innerGlow.style.display = 'none'
      }
    })

    // 轉成 Base64 圖檔連結並觸發下載
    const image = canvas.toDataURL('image/png')
    const link = document.createElement('a')
    link.href = image
    link.download = `${filenamePrefix}-${Date.now()}.png`
    link.click()
  } catch (error) {
    console.error('色卡匯出失敗:', error)
    alert('匯出圖片時發生問題,請稍候再試!')
  } finally {
    isExporting.value = false
  }
}

三、修改 <template> 加入按鈕與大圖彈窗

1. 入口 A:在「本月微光色卡彈窗」加入下載按鈕

在原本的月度色卡彈窗中,做兩處小調整:

  • 卡片容器加上 ref="currentCardRef"。
  • 操作按鈕區加上 data-html2canvas-ignore="true",確保下載下來的圖片不會印出網頁按鈕:
<div class="monthly-art-card" ref="currentCardRef" @click.stop>
  <!-- 畫布主體與下方文字維持原樣 ... -->

  <!-- 操作按鈕群組:珍藏與下載 -->
  <div class="card-modal-actions" data-html2canvas-ignore="true">
    <button class="btn-seal" @click="sealMonthlyCard">珍藏至時光相簿</button>
    <button 
      class="btn-download" 
      :disabled="isExporting"
      @click="exportCardAsImage(currentCardRef, `mood-${cardMonthTarget}`)"
    >
      {{ isExporting ? '匯出中...' : '儲存為圖片' }}
    </button>
  </div>
</div>

2. 在相簿列表綁定點擊事件

找到時光相簿清單中的小卡片,加上點擊展開事件與樣式 class:

<div 
  v-for="card in archivedCards" 
  :key="card.month" 
  class="album-card-item is-clickable"
  @click="openCardDetail(card)"
>

3. 入口 B:新增「相簿歷史色卡大圖檢視與下載彈窗」

在時光相簿彈窗外層下方,新增獨立的大圖檢視彈窗。結構與原本色卡相同,資料來源讀取 selectedAlbumCard,並提供大圖下載功能:

<!-- 3. 相簿歷史色卡大圖檢視彈窗 -->
<div v-if="selectedAlbumCard" class="modal-backdrop z-top" @click="closeCardDetail">
  <div class="monthly-art-card" ref="detailCardRef" @click.stop>
    <!-- 卡片內容結構與月度色卡相同,維持原樣(僅資料來源綁定 selectedAlbumCard) ... -->

    <!-- 操作列:只有下載與關閉 -->
    <div class="card-modal-actions" data-html2canvas-ignore="true">
      <button 
        class="btn-download primary" 
        :disabled="isExporting"
        @click="exportCardAsImage(detailCardRef, `mood-${selectedAlbumCard.month}`)"
      >
        {{ isExporting ? '匯出中...' : '儲存為圖片' }}
      </button>
      <button class="btn-cancel" @click="closeCardDetail">關閉</button>
    </div>
  </div>
</div>

四、加入樣式

在 <style scoped> 尾部補上按鈕與彈窗層級樣式:

/* 操作按鈕並排群組 */
.card-modal-actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
}

/* 下載按鈕樣式 */
.btn-download {
  background: rgba(31, 35, 40, 0.08);
  border: 1px solid rgba(31, 35, 40, 0.2);
  color: #24292f;
  padding: 8px 16px;
  border-radius: 20px;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-download:hover {
  background: rgba(31, 35, 40, 0.15);
}

.btn-download.primary {
  background: #1f2328;
  color: #ffffff;
  border: none;
}

.btn-download.primary:hover {
  opacity: 0.9;
}

.btn-download:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-cancel {
  background: none;
  border: none;
  color: #57606a;
  font-size: 0.8rem;
  padding: 8px 14px;
  cursor: pointer;
}

/* 相簿卡片點擊手勢 */
.album-card-item.is-clickable {
  cursor: pointer;
}

/* 確保大圖彈窗疊在相簿彈窗最上層 */
.modal-backdrop.z-top {
  z-index: 200;
}

做完這邊就可以到瀏覽器看看今天的大重點「下載圖片」,能不能操作成功!

五、驗收測試流程

  1. 測試入口 A(即時色卡下載):
  • 點選「查看本月微光色卡」。
  • 點擊「儲存為圖片」,檢查瀏覽器是否順利觸發 PNG 圖檔下載。

https://ithelp.ithome.com.tw/upload/images/20261006/20178067LAy45UDSi4.png

  1. 測試入口 B(相簿回放與下載):
  • 點擊「時光相簿」,點選相簿裡的微縮色卡。
  • 確認色卡能順利以原尺寸大圖展開。
  • 點擊「儲存為圖片」,確認歷史卡片同樣能正確輸出。

https://ithelp.ithome.com.tw/upload/images/20261006/20178067FoZuT5P0ed.png

六、實作挑戰:按鈕點擊完全失效

在測試色卡彈窗時,遇到了一個棘手的問題:畫面上的按鈕完全點不進去,點擊毫無反應,也沒有觸發任何下載或關閉行為。

1. 問題排查

打開瀏覽器開發者工具,發現主控台拋出了多條紅色錯誤警告:

https://ithelp.ithome.com.tw/upload/images/20261006/20178067xmw975CioX.png

2. 原因分析

在計算屬性 monthlyGradientStyle 中,使用了 dominant.color,但函式內漏掉了 const dominant = stats[0] 的宣告。當模板重新渲染並執行這個計算屬性時,會拋出 ReferenceError,造成 Vue 的渲染流程發生錯誤,因此畫面上的互動也受到影響,出現按鈕完全無法點擊的情況。

3. 解決方案

在 monthlyGradientStyle 與 getCardGradientStyle 讀取屬性前,確保變數正確宣告並賦值:

	// 補上主導色變數宣告,避免未定義錯誤導致渲染中斷
  const dominant = stats[0]

明日預告

今天我們順利完成了色卡的大圖檢視與高畫質圖片匯出,讓每個月的生活印記都能保存成一張專屬的圖片。

不過目前專案一開啟就直接進入微光採集瓶,少了一個能讓使用者認識「微光日常」的入口與儀式感。明天(Day 23)我要打造一個「首頁」,加入簡單的情境引導與「開啟微光採集瓶」入口,並搭配視圖轉場效果,讓使用者能更自然地進入微光日常的主畫面,進一步完善整體使用體驗。


上一篇
【Day 21】打造時光相簿:將月度記憶留存為小圖卡
系列文
微光採集瓶:用 Vue 3 打造每日美好記錄網頁 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言