「微光採集瓶」最重要的核心,不只是收集,而是重溫記憶。當初在輸入框裡寫下的那句「今天的美好小事」,如果存進瓶子後就再也看不到了,那過一陣子之後很有可能會忘記,當初發生了什麼令自己感到幸福的事情,我覺得有點可惜,美好的記憶應該要一直存在腦海裡。
所以今天我們要讓使用者可以跟瓶子裡的光珠互動:滑鼠移上去時有微光反應,點擊後彈出小卡片,重新顯示出那天寫下的那道微光。
在實作之前,先思考使用者操作的重點:
光珠太小很難點(操作提示不足):
光珠的尺寸只有 14px,在畫面上非常小。如果滑鼠移上去沒有任何變化,使用者根本不會知道「原來這顆球可以點」。所以我們需要加上 :hover 效果,讓珠子在滑鼠靠近時稍微放大、加強光暈,並換上 cursor: pointer 的手勢。
怎麼展示內容(Modal 彈出視窗):
我們不需要複雜的路由跳轉,只要一個簡單的浮層(Modal)。點擊光珠時展示它、點擊背景或關閉按鈕時收起它。
在 Vue 裡,處理彈窗最經典的方式就是 「單一狀態控制」。
<script setup> 增加選中狀態打開 src/components/GemJar.vue。我們只需要宣告一個響應式變數 activeGem,用來記錄「當前被點擊的光珠物件」:
// 記錄目前被點選要查看的光珠(沒點選時為 null)
const activeGem = ref(null)
// 開啟預覽卡片
function openGem(gem) {
activeGem.value = gem
}
// 關閉預覽卡片
function closeGem() {
activeGem.value = null
}
這個邏輯非常簡單:
activeGem 的值是 null。note、color、id)傳進來存著。null。接著到 <template> 區塊,我們做兩件事:
.gem-dot 上綁定點擊事件 @click="openGem(gem)"。v-if="activeGem" 控制的彈出視窗。<!-- 裝著所有光珠的區域 -->
<div class="gem-pile">
<div
v-for="gem in gems"
:key="gem.id"
class="gem-dot"
:style="{
backgroundColor: gem.color,
'--offset-x': `${gem.offsetX}px`,
'--offset-y': `${gem.offsetY}px`
}"
@click="openGem(gem)"
></div>
</div>
<!-- 彈出預覽卡片 -->
<div v-if="activeGem" class="modal-backdrop" @click="closeGem">
<div class="gem-card" :style="{ borderColor: activeGem.color }" @click.stop>
<!-- 小光點裝飾,沿用該光珠的顏色 -->
<div class="gem-badge" :style="{ backgroundColor: activeGem.color }"></div>
<!-- 當時寫下的文字 -->
<p class="gem-text">{{ activeGem.note }}</p>
<button class="btn-close" @click="closeGem">收起微光</button>
</div>
</div>
Vue 實用小技巧:什麼是
@click.stop?在外層的黑底遮罩
.modal-backdrop上,我們綁定了@click="closeGem",意思是「點擊背景空白處就關閉彈窗」。但如果不做任何處理,點擊中間的白色卡片時,點擊事件會冒泡(Bubbling)傳遞到外層背景,導致點擊卡片看文字時卡片反而意外關閉。
只要在卡片容器加上
@click.stop(等同於原生 JS 的event.stopPropagation()),就能阻擋點擊事件往外冒泡。
最後,在 <style scoped> 裡加上視覺效果:
/* 加上指標手勢 */
.gem-dot {
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* 滑鼠懸浮時:稍微放大、光暈增強 */
.gem-dot:hover {
transform: translate(var(--offset-x, 0), var(--offset-y, 0)) scale(1.4) !important;
box-shadow: 0 0 12px rgba(255, 255, 255, 0.8);
z-index: 10;
}
/* 遮罩背景 */
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.65);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
z-index: 100;
}
/* 預覽卡片本體 */
.gem-card {
background: #1e2228;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 24px;
width: 240px;
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
/* 卡片上方的心情光點 */
.gem-badge {
width: 12px;
height: 12px;
border-radius: 50%;
margin: 0 auto 16px auto;
box-shadow: 0 0 8px currentColor;
}
/* 採集時記錄的心情文字 */
.gem-text {
color: #f0f6fc;
font-size: 0.95rem;
line-height: 1.6;
margin: 0 0 20px 0;
word-break: break-all;
}
/* 關閉按鈕 */
.btn-close {
background: rgba(255, 255, 255, 0.1);
border: none;
color: #8b949e;
padding: 6px 18px;
border-radius: 16px;
cursor: pointer;
font-size: 0.8rem;
transition: background 0.2s, color 0.2s;
}
.btn-close:hover {
background: rgba(255, 255, 255, 0.2);
color: #ffffff;
}



存檔後回到瀏覽器測試:
activeGem,控制彈窗的開啟、關閉與內容綁定。:hover 放大與游標提示,解決小元素不易點選的問題,大幅提升介面操作友善性。@click.stop阻擋事件冒泡,避免點擊彈窗內容時觸發背景的關閉邏輯。現在光珠不僅能採集、自然堆疊,還能隨時被點開回味了!
但如果重新整理瀏覽器,這些珍貴的記憶就會全部消失。明天(Day 14),我們要為採集瓶加上「本地持久化存儲」,讓你的每一道微光,即使關閉分頁也不會消散。