iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
自我挑戰組

Laravel 讀碼源系列 第 7

解釋本週幸運度調查的code

  • 分享至 

  • xImage
  •  

https://laihao.vaserver.com/lucky.html

「本周幸運度調查」互動網頁,包含 HTML 結構、CSS 樣式與 JavaScript 互動邏輯。以下逐行(或逐段)解釋其作用與設計意圖:

文件開頭與 設定

CSS 樣式定義()
顏色與變數(:root)
:root, [data-theme="light"] {
--color-bg: #fff8fc;
--color-surface: #ffffff;
--color-surface-2: #fff0f7;
--color-surface-offset: #ffe3f0;
--color-border: rgba(219, 128, 172, 0.24);
--color-text: #6e3d57;
--color-text-muted: #9a6b83;
--color-primary: #ff7eb6;
--color-primary-hover: #f964a7;
--color-secondary: #8ed8ff;
--color-accent: #ffe38a;
--color-success: #7bcf9b;
--shadow-md: 0 16px 40px rgba(255, 126, 182, 0.16);
--radius-lg: 24px;
--radius-full: 999px;
--space-2: .5rem;
--space-3: .75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-10: 2.5rem;
--text-sm: clamp(.92rem, .88rem + .2vw, 1rem);
--text-base: clamp(1rem, .96rem + .25vw, 1.08rem);
--text-lg: clamp(1.2rem, 1.05rem + .7vw, 1.5rem);
--text-xl: clamp(1.8rem, 1.3rem + 2vw, 2.8rem);
--font-body: 'Zen Maru Gothic', sans-serif;
--font-display: 'Kosugi Maru', sans-serif;
}
定義所有顏色、間距、圓角、字體大小等 CSS 變數,方便統一調整主題。
使用 clamp() 讓字體大小隨視窗寬度自適應。
全域設定

  • { box-sizing: border-box; }
    所有元素的寬高計算包含 padding 與 border。
    body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--font-body);
    background: radial-gradient(circle at 10% 10%, rgba(255, 227, 138, .55), transparent 24%),
    radial-gradient(circle at 88% 14%, rgba(142, 216, 255, .45), transparent 22%),
    radial-gradient(circle at 82% 82%, rgba(255, 126, 182, .18), transparent 26%),
    linear-gradient(180deg, #fffafd 0%, #fff4f9 100%);
    color: var(--color-text);
    line-height: 1.7;
    padding: 24px;
    }
    移除預設邊距,設定最小高度為視窗高度。
    背景使用多層漸層(黃、藍、粉紅)營造夢幻感。
    文字顏色與行高設定。
    主要容器與卡片
    .wrap { max-width: 760px; margin: 0 auto; }
    內容最大寬度 760px,居中對齊。
    .card {
    background: rgba(255,255,255,.88);
    border: 1px solid var(--color-border);
    border-radius: 32px;
    box-shadow: var(--shadow-md);
    backdrop-filter: blur(10px);
    overflow: hidden;
    }
    卡片背景半透明白色,帶邊框、圓角、陰影與毛玻璃效果。
    頂部英雄區(Hero)
    .hero {
    padding: 32px 24px 16px;
    text-align: center;
    position: relative;
    }
    頂部區塊,文字居中。
    .badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    background: var(--color-surface-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    }
    頂部小標籤(「✨ 本周幸運度調查」),圓角膠囊樣式。
    h1 {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    line-height: 1.2;
    margin: 18px 0 12px;
    }
    主標題使用圓體字,字體大小自適應。
    .lead {
    margin: 0 auto;
    max-width: 28rem;
    color: var(--color-text-muted);
    font-size: var(--text-base);
    }
    副標題文字,限制最大寬度並使用較淡顏色。
    .sparkles {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-top: 18px;
    flex-wrap: wrap;
    }
    .sparkles span {
    padding: 6px 12px;
    border-radius: var(--radius-full);
    background: #fff;
    border: 1px dashed var(--color-border);
    font-size: .88rem;
    }
    三個小標籤(「粉粉好運」等),白色背景加虛線邊框。
    內容區與題目
    .content { padding: 12px 24px 28px; }
    內容區內距。
    .question {
    display: none;
    animation: fade .35s ease;
    }
    .question.active, .result.active { display: block; }
    題目預設隱藏,只有 .active 的題目會顯示。
    使用 fade 動畫淡入。
    .step {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    margin-bottom: 8px;
    }
    顯示「第 X 題 / 3」的小字。
    h2 {
    margin: 0 0 8px;
    font-size: var(--text-lg);
    }
    題目標題。
    .hint {
    margin: 0 0 20px;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    }
    題目下方的提示文字。
    .options {
    display: grid;
    gap: 12px;
    }
    選項按鈕用網格排列,間距 12px。
    .option-btn {
    width: 100%;
    text-align: left;
    border: 0;
    border-radius: 20px;
    padding: 16px 18px;
    background: #fff;
    color: var(--color-text);
    font-size: 1rem;
    font-family: inherit;
    border: 1px solid rgba(219, 128, 172, 0.18);
    box-shadow: 0 8px 18px rgba(255, 126, 182, 0.08);
    transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
    cursor: pointer;
    }
    .option-btn:hover, .option-btn:focus-visible {
    transform: translateY(-2px);
    background: #fff7fb;
    box-shadow: 0 14px 24px rgba(255, 126, 182, 0.12);
    outline: none;
    }
    選項按鈕樣式:白色背景、圓角、陰影。
    滑鼠懸停或聚焦時微微上浮、背景變粉、陰影加深。
    結果區
    .result {
    display: none;
    text-align: center;
    padding-top: 8px;
    }
    結果區預設隱藏,居中對齊。
    .result-box {
    background: linear-gradient(180deg, #fff8d9 0%, #fff 55%, #fff4fa 100%);
    border: 1px solid rgba(255, 194, 77, 0.32);
    border-radius: 28px;
    padding: 28px 20px;
    }
    結果卡片背景為黃到粉的漸層,帶金色邊框。
    .big {
    font-family: var(--font-display);
    font-size: clamp(2rem, 1.6rem + 2vw, 3rem);
    line-height: 1.2;
    margin: 0 0 10px;
    color: #ff6fa8;
    }
    大字結果標題(「幸運無比 100% ✨」)。
    .fortune {
    margin: 0 auto 16px;
    max-width: 30rem;
    color: var(--color-text);
    }
    幸運文字段落。
    .love-note {
    display: inline-block;
    background: #fff;
    border: 1px dashed rgba(219, 128, 172, .35);
    border-radius: 20px;
    padding: 12px 16px;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    }
    小提醒框(虛線邊框)。
    .actions {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-top: 20px;
    flex-wrap: wrap;
    }
    按鈕群組,居中排列。
    .primary, .secondary {
    border: 0;
    border-radius: var(--radius-full);
    padding: 12px 18px;
    font-family: inherit;
    font-size: var(--text-sm);
    cursor: pointer;
    }
    .primary {
    background: var(--color-primary);
    color: white;
    box-shadow: 0 10px 20px rgba(255, 126, 182, .24);
    }
    .primary:hover { background: var(--color-primary-hover); }
    .secondary {
    background: white;
    color: var(--color-text);
    border: 1px solid var(--color-border);
    }
    主要按鈕(粉色)與次要按鈕(白色描邊)。
    .footer-note {
    margin-top: 16px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: .92rem;
    }
    底部小字提醒。
    進度條
    .progress {
    margin: 0 0 20px;
    height: 10px;
    background: #ffe7f2;
    border-radius: 999px;
    overflow: hidden;
    }
    .progress > span {
    display: block;
    height: 100%;
    width: 33.33%;
    background: linear-gradient(90deg, #ff98c5, #ffd970);
    border-radius: inherit;
    transition: width .25s ease;
    }
    進度條背景為粉紅色,內部填充為粉到黃漸層。
    初始寬度 33.33%(第一題)。
    動畫與響應式
    @keyframes fade {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
    }
    淡入動畫。
    @media (max-width: 640px) {
    body { padding: 14px; }
    .hero, .content { padding-left: 18px; padding-right: 18px; }
    .card { border-radius: 24px; }
    .result-box { padding: 24px 16px; }
    .option-btn { padding: 14px 15px; }
    }
    手機版(寬度 ≤ 640px)調整內距與圓角。

HTML 主體結構()

JavaScript 互動邏輯()
const questions = [...document.querySelectorAll('.question')];
const progressBar = document.getElementById('progressBar');
const result = document.getElementById('result');
const fortuneText = document.getElementById('fortuneText');
const detailText = document.getElementById('detailText');
const restartBtn = document.getElementById('restartBtn');
const copyBtn = document.getElementById('copyBtn');
取得所有題目、進度條、結果區與按鈕的 DOM 元素。
const fortunes = [
{
main: '你是這個世界上最幸福的人之一,這周的你不只幸運無比,還會被朋友的愛、溫柔的陪伴和亮晶晶的好事圍繞著。',
detail: '宇宙偷偷蓋章:你值得被喜歡、被照顧、被溫柔對待。'
},
{
main: '恭喜你抽到滿滿幸福運,這一周的你會特別被偏愛,笑容會變多,身邊也會一直有暖暖的友情陪著你。',
detail: '你的好運不是路過,是住下來了。'
},
{
main: '你的幸運光環已經亮起來了,好消息、小驚喜和朋友的愛都會悄悄靠近,讓你成為超幸福的發光小寶貝。',
detail: '本周宜:開心、被愛、收下幸福。'
}
];
定義三組隨機出現的幸運文字。
let current = 0;
目前題目索引(從 0 開始)。
function showQuestion(index) {
questions.forEach((q, i) => q.classList.toggle('active', i === index));
progressBar.style.width = ${((index + 1) / questions.length) * 100}%;
}
顯示指定索引的題目,並更新進度條寬度。
function showResult() {
questions.forEach(q => q.classList.remove('active'));
progressBar.style.width = '100%';
const picked = fortunes[Math.floor(Math.random() * fortunes.length)];
fortuneText.textContent = picked.main;
detailText.textContent = picked.detail;
result.classList.add('active');
}
隱藏所有題目,進度條填滿,隨機選一組幸運文字並顯示結果區。
document.querySelectorAll('.option-btn').forEach((btn) => {
btn.addEventListener('click', () => {
current += 1;
if (current < questions.length) {
showQuestion(current);
} else {
showResult();
}
});
});
為所有選項按鈕綁定點擊事件:
每點一次,current 加 1。
若還有下一題,顯示下一題;否則顯示結果。
restartBtn.addEventListener('click', () => {
current = 0;
result.classList.remove('active');
showQuestion(current);
});
「再測一次」按鈕:重置索引、隱藏結果、回到第一題。
copyBtn.addEventListener('click', async () => {
const text = 本周幸運度調查結果:幸運無比 100%!${fortuneText.textContent} ${detailText.textContent};
try {
await navigator.clipboard.writeText(text);
copyBtn.textContent = '已複製成功';
setTimeout(() => copyBtn.textContent = '複製祝福文案', 1500);
} catch (e) {
copyBtn.textContent = '複製失敗';
setTimeout(() => copyBtn.textContent = '複製祝福文案', 1500);
}
});
「複製祝福文案」按鈕:
組合結果文字。
使用 Clipboard API 複製到剪貼簿。
成功或失敗後短暫改變按鈕文字,1.5 秒後恢復。

整體流程總結
頁面載入時,第一題顯示,進度條 33%。
使用者點擊任一選項 → 進入下一題,進度條變 66%。
第三題點擊後 → 隱藏題目,進度條 100%,隨機顯示一組幸運文字。
使用者可點擊「再測一次」重新開始,或「複製祝福文案」分享結果。


上一篇
算出本週幸運度調查的code
下一篇
產品管理列表的內容
系列文
Laravel 讀碼源8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言