移动端 H5 签到解决方案:自适应布局与完整交互逻辑
在移动端活动开发中,每日签到是积分体系、会员运营及用户留存的核心组件。本文分享一套可直接上线的移动端 H5 签到解决方案,具备全屏自适应、禁止横向滚动、视觉精致及交互完整等特性。
效果演示:
全屏自适应,禁止横向滚动
周一至周日 7 个签到格子自动均分屏幕宽度。无论大屏或小屏手机,内容均能完整展示,无文字挤压、无溢出,无需滑动查看。
状态分层,视觉清晰
- 未签到:浅粉渐变卡片,搭配浅色星星图标,低透明度弱化展示。
- 当日可签:橙色高亮边框加微光阴影,醒目突出当前签到日。
- 已签到:金色渐变填充卡片,白色星光图标,完成状态一目了然。
完整代码实现
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>积分乐园</title><style>* {margin: 0;padding: 0;box-sizing: border-box;font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;}body {background: #F7F8FA;min-height: 100vh;overflow-x: hidden;}.header-banner {width: 100%;overflow: hidden;}.header-banner img {width: 100%;height: 100%;object-fit: cover;object-position: center top;display: block;}.score-card {background: #fff;border-radius: 20px;padding: 18px;margin: -18px 16px 12px;position: relative;z-index: 2;box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);}.week-sign-wrap {background: #FFF7ED;border-radius: 14px;padding: 16px 12px;position: relative;z-index: 2;}.week-sign-title {font-size: 16px;font-weight: 700;color: #333;margin-bottom: 14px;display: flex;align-items: center;gap: 6px;}.week-sign-title::before {content: "📅";font-size: 16px;}.week-sign-scroll {width: 100%;}.week-sign-grid {display: flex;gap: 4px;margin-bottom: 14px;}.week-day-item {flex: 1;background: transparent;border-radius: 0;border: none;display: flex;flex-direction: column;align-items: center;justify-content: flex-start;position: relative;padding: 0;box-shadow: none;transition: transform 0.15s, box-shadow 0.15s;}.week-day-item:active {transform: scale(0.95);}.coin-card {width: 100%;aspect-ratio: 1 / 0.75;background: linear-gradient(135deg, #FFF1F3, #FFE4E9);border-radius: 10px;border: 1px solid #FFD6DD;display: flex;flex-direction: column-reverse;align-items: center;justify-content: center;padding: 4px 2px;box-shadow: 0 2px 6px rgba(255,122,0,0.06);margin-bottom: 6px;opacity: 0.65;gap:2px;}.week-day-item.today .coin-card {border: 2px solid #ff7a00;box-shadow: 0 0 10px rgba(255,122,0,0.2);opacity: 1;}.week-day-name {font-size: 11px;color: #666;margin-bottom: 0;text-align: center;white-space: nowrap;}.star-svg-wrap{width:18px;height:18px;}.star-svg-wrap svg{width:100%;height:100%;}.week-day-item:not(.signed) .star-path{fill:#ffbcac;}.week-day-item.signed .star-path{fill:#ffffff;filter: drop-shadow(0 2px 4px rgba(255,140,0,0.45));}.reward-text {font-size: 11px;color: #ff7a00;font-weight: 600;}.week-day-item.signed .reward-text {color: #fff;}.week-day-item.signed .coin-card {background: linear-gradient(135deg, #ffc837, #ff9500);opacity: 1;}.sign-btn {width: 100%;height: 48px;border: none;border-radius: 24px;background: linear-gradient(90deg, #FF7A00, #FF9A2E);color: #fff;font-size: 17px;font-weight: 700;cursor: pointer;box-shadow: 0 6px 16px rgba(255, 122, 0, 0.25);transition: transform 0.15s, box-shadow 0.15s;letter-spacing: 2px;}.sign-btn:disabled {background: #cccccc;box-shadow: none;cursor: not-allowed;letter-spacing: 0;}.sign-btn:active:not(:disabled) {transform: scale(0.97);box-shadow: 0 3px 8px rgba(255, 122, 0, 0.2);}.sign-tip-capsule {margin-top: 12px;width: 100%;background: rgba(255,122,0,0.08);border-radius: 20px;padding: 8px 16px;display: flex;align-items: center;justify-content: space-between;gap: 8px;}.tip-left {font-size: 12px;color: #FF7A00;font-weight: 600;white-space: nowrap;}.tip-right-wrap {font-size: 12px;color: #B8895F;font-weight: 500;white-space: nowrap;display:flex;align-items:center;gap:4px;text-decoration:none;}.arrow-icon{font-size:14px;color:#B8895F;}.container {padding: 0 16px;}.quick-grid {display: grid;grid-template-columns: repeat(2, 1fr);gap: 12px;margin: 16px 0;}.quick-card {border-radius: 16px;padding: 18px 16px;display: flex;flex-direction: column;justify-content: space-between;min-height: 110px;transition: transform 0.15s;}.quick-card:active {transform: scale(0.97);}.quick-card.orange {background: linear-gradient(135deg, #FFF7ED, #FFE8D6);}.quick-card.blue {background: linear-gradient(135deg, #F0F7FF, #DCEFFF);}.quick-card-title {font-size: 16px;font-weight: 700;color: #333;margin-bottom: 6px;display: flex;align-items: center;gap: 6px;}.quick-card.orange .quick-card-title::before {content: "🛍️";}.quick-card.blue .quick-card-title::before {content: "🎁";}.quick-card-sub {font-size: 13px;color: #666;}.quick-btn {align-self: flex-start;border: none;border-radius: 22px;padding: 8px 18px;font-size: 14px;font-weight: 600;cursor: pointer;margin-top: 12px;transition: transform 0.15s;}.quick-btn:active {transform: scale(0.95);}.quick-btn.orange {background: #ff7a00;color: #fff;}.quick-btn.blue {background: #4080FF;color: #fff;}@media (min-width: 768px) {body {background-color: #e9ecef;}.app-wrap-pc {max-width: 440px;margin: 30px auto;}.header-banner,.score-card,.container {max-width: 440px;margin-left: auto;margin-right: auto;}.header-banner {border-radius: 16px 16px 0 0;overflow: hidden;}.score-card {margin-top: -18px;border-radius: 0 0 16px 16px;}.week-day-item {max-width: 64px;}.coin-card {max-height: 52px;}.sign-btn {max-height: 48px;}}</style></head><body><div class="app-wrap-pc"><div class="header-banner"><img src="https://mmbiz.qpic.cn/mmbiz_png/ZXQtRibNoDwUu6G7kORiaB9COibdfIUkfOsA8Z4W9CFRGOd5pUegIx1O2nUOPKDd8tDhuiaMaFenEicR1661EKl38eibiaaHE1bBadiapcGNo752GIk/640?wx_fmt=png&from=appmsg" alt="积分乐园" /></div><div class="score-card"><div class="week-sign-wrap"><div class="week-sign-title">本周签到</div><div class="week-sign-scroll"><div class="week-sign-grid" id="weekSignGrid"></div></div><button class="sign-btn" id="signBtn">立即签到</button><div class="sign-tip-capsule"><span class="tip-left">签到领积分,累计赢好礼</span><a href="#/" class="tip-right-wrap">我的积分 <span id="totalPointsCapsule">13,203</span><span class="arrow-icon">></span></a></div></div></div><div class="container"><div class="quick-grid"><div class="quick-card orange"><div><div class="quick-card-title">积分换购</div><div class="quick-card-sub">好物限时低价兑换</div></div><button class="quick-btn orange">立即进入</button></div><div class="quick-card blue"><div><div class="quick-card-title">积分抽奖</div><div class="quick-card-sub">消耗积分抽好礼</div></div><button class="quick-btn blue">去抽奖</button></div></div></div><script>const signBtn = document.getElementById("signBtn");const weekSignGrid = document.getElementById("weekSignGrid");const totalPointsCapsule = document.getElementById("totalPointsCapsule");const weekDays = [{ name: "周一", reward: "+5" },{ name: "周二", reward: "+5" },{ name: "周三", reward: "+5" },{ name: "周四", reward: "+5" },{ name: "周五", reward: "+7" },{ name: "周六", reward: "+9" },{ name: "周日", reward: "+10" }];function getStorage() {const data = localStorage.getItem("pointParkData");return data ? JSON.parse(data) : {totalPoints: 13203,signList: [],continueDays: 0};}function saveStorage(obj) {localStorage.setItem("pointParkData", JSON.stringify(obj));}function getTodayStr() {const d = new Date();const y = d.getFullYear();const m = String(d.getMonth() + 1).padStart(2, "0");const day = String(d.getDate()).padStart(2, "0");return `${y}-${m}-${day}`;}function getThisWeekDate() {const now = new Date();const day = now.getDay();const monday = new Date(now.getTime() - (day === 0 ? 6 : day - 1) * 24 * 60 * 60 * 1000);return weekDays.map((item, i) => {const tmp = new Date(monday.getTime() + i * 24 * 60 * 60 * 1000);const y = tmp.getFullYear();const m = String(tmp.getMonth() + 1).padStart(2, "0");const d = String(tmp.getDate()).padStart(2, "0");return {name: item.name,reward: item.reward,dateStr: `${y}-${m}-${d}`};});}function renderWeekSign() {const data = getStorage();const weekList = getThisWeekDate();weekSignGrid.innerHTML = "";weekList.forEach(item => {const isSigned = data.signList.includes(item.dateStr);const isToday = item.dateStr === getTodayStr();const div = document.createElement("div");div.className = "week-day-item";if (isSigned) div.classList.add("signed");if (isToday) div.classList.add("today");div.innerHTML = `<div class="coin-card"><div class="star-svg-wrap"><svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path class="star-path" d="M512 85.333l125.867 254.293 281.6 40.96-203.094 198.827 47.954 280.879L512 725.333 259.673 860.292l47.954-280.879L104.533 380.586l281.6-40.96L512 85.333z"></path></svg></div><div class="reward-text">${item.reward}</div></div><div class="week-day-name">${item.name}</div>`;weekSignGrid.appendChild(div);});}function refreshPage() {const data = getStorage();const today = getTodayStr();totalPointsCapsule.innerText = data.totalPoints.toLocaleString();if (data.signList.includes(today)) {signBtn.disabled = true;signBtn.innerText = "今日已签到";} else {signBtn.disabled = false;signBtn.innerText = "立即签到";}renderWeekSign();}signBtn.onclick = function () {const data = getStorage();const today = getTodayStr();if (data.signList.includes(today)) return;data.signList.push(today);const now = new Date();const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);const yStr = `${yesterday.getFullYear()}-${String(yesterday.getMonth() + 1).padStart(2, "0")}-${String(yesterday.getDate()).padStart(2, "0")}`;if (data.signList.includes(yStr)) {data.continueDays += 1;} else {data.continueDays = 1;}let addPoint = 10;if (data.continueDays === 3) addPoint += 5;if (data.continueDays === 7) addPoint += 10;data.totalPoints += addPoint;saveStorage(data);refreshPage();alert(`签到成功!获得 ${addPoint} 积分`);};refreshPage();</script></body></html>
核心功能特性
- 自动计算本周日期:动态渲染周一至周日对应日期,无需手动写死。
- 智能签到判定:自动识别今日是否已签到,按钮状态实时切换。
- 连续签到奖励机制:支持普通签到、3 天连续、7 天连续额外积分加成。
- 积分自动累计:积分格式化千分位展示,数据呈现更专业。
- 本地缓存记录:签到记录、连续天数、总积分实现本地持久化。
- 辅助功能模块:提供积分换购、积分抽奖快捷入口,形成业务闭环。
适用场景
- 商城 H5 页面
- 会员中心
- 积分乐园
- 活动留存页
- 每日任务体系
#签到功能 #积分系统 #前端实战

