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

只有前端
route的地方
這是一個「本週幸運度調查」的單頁互動網頁,用 HTML + CSS + 原生 JavaScript 實作,核心流程是:顯示 3 題選擇題 → 根據選擇進度切換題目 → 最後隨機顯示一個幸運結果,並提供「再測一次」與「複製祝福文案」功能。
程式碼
<!DOCTYPE html> <html lang="zh-Hant" data-theme="light"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>本周幸運度調查</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Kosugi+Maru&family=Zen+Maru+Gothic:wght@400;500;700&display=swap" rel="stylesheet"> <style> :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; } * { box-sizing: border-box; } 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; } .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 { 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; } .step { font-size: var(--text-sm); color: var(--color-text-muted); margin-bottom: 8px; } 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; } .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; } .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; } @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; } } </style> </head> <body> <main class="wrap"> <section class="card" aria-labelledby="page-title"> <div class="hero"> <div class="badge">✨ 本周幸運度調查</div> <h1 id="page-title">三個小問題,測出你是不是被幸福偷偷抱住的人</h1> <p class="lead">選你喜歡的小東西就好,不管怎麼選,你都是這周閃閃發亮、被朋友的愛圍繞的幸運寶寶。</p> <div class="sparkles" aria-hidden="true"> <span>粉粉好運</span> <span>朋友環繞</span> <span>幸福發光</span> </div> </div> <div class="content"> <div class="progress" aria-label="進度條"><span id="progressBar"></span></div> <section class="question active" data-step="1"> <div class="step">第 1 題 / 3</div> <h2>你最喜歡的顏色是?</h2> <p class="hint">每一種顏色,都藏著專屬你的可愛幸運。</p> <div class="options"> <button class="option-btn" type="button">粉色</button> <button class="option-btn" type="button">藍色</button> <button class="option-btn" type="button">黃色</button> <button class="option-btn" type="button">綠色</button> <button class="option-btn" type="button">紫色</button> </div> </section> <section class="question" data-step="2"> <div class="step">第 2 題 / 3</div> <h2>你最想和哪種寵物當朋友?</h2> <p class="hint">不管是軟萌、活潑,還是慢慢可愛,都是幸福訊號。</p> <div class="options"> <button class="option-btn" type="button">貓咪</button> <button class="option-btn" type="button">狗狗</button> <button class="option-btn" type="button">烏龜</button> </div> </section> <section class="question" data-step="3"> <div class="step">第 3 題 / 3</div> <h2>你今天最想聽哪一類音樂?</h2> <p class="hint">你喜歡的旋律,正在替這周的好運加分。</p> <div class="options"> <button class="option-btn" type="button">浪漫抒情</button> <button class="option-btn" type="button">快樂流行</button> <button class="option-btn" type="button">輕鬆療癒</button> <button class="option-btn" type="button">活力舞曲</button> </div> </section> <section class="result" id="result"> <div class="result-box"> <p class="step">你的本周結果出爐啦</p> <h2 class="big">幸運無比 100% ✨</h2> <p class="fortune" id="fortuneText"></p> <div class="love-note" id="detailText"></div> <div class="actions"> <button class="primary" type="button" id="restartBtn">再測一次</button> <button class="secondary" type="button" id="copyBtn">複製祝福文案</button> </div> </div> <p class="footer-note">今天的你,自帶幸運小光點,值得好多好多快樂。</p> </section> </div> </section> </main> <script> 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'); const fortunes = [ { main: '你是這個世界上最幸福的人之一,這周的你不只幸運無比,還會被朋友的愛、溫柔的陪伴和亮晶晶的好事圍繞著。', detail: '宇宙偷偷蓋章:你值得被喜歡、被照顧、被溫柔對待。' }, { main: '恭喜你抽到滿滿幸福運,這一周的你會特別被偏愛,笑容會變多,身邊也會一直有暖暖的友情陪著你。', detail: '你的好運不是路過,是住下來了。' }, { main: '你的幸運光環已經亮起來了,好消息、小驚喜和朋友的愛都會悄悄靠近,讓你成為超幸福的發光小寶貝。', detail: '本周宜:開心、被愛、收下幸福。' } ]; let current = 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(); } }); }); 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); } }); </script> </body> </html>
明天就來解釋~