/* ================= 首页操作引导(2026-10-12) ================= 用户:「给首页加上引导操作步骤」。第一次进首页时,一步一步高亮讲 5 个地方: ① 头图孕周 ② 孕育工具 ③ 此刻 ④「记一下」圆钮 ⑤「我的」 · 只走一次:看完或点「跳过」都记 localStorage 'mmbox.coach.v1',以后不再出(同 onboarding-once-rule)。 · 调试 / 给人看:URL 带 ?coach=1 每次都出。 · 不抢别的弹层:授权、欢迎礼、孕期档案、记一下面板、详情页等开着时不计时, 全部关掉、首页安静 1.5 秒后才开始;引导进行中别的弹层冒出来 → 暂时收起,等它关了从当前步接着讲。 · 某一步的目标不在页面上(如「此刻」接口没数据整块隐藏)→ 自动跳过这一步,不留空框。 · 遮罩挂在 #phone 里(跟页面同一个坐标系),z-index 高于所有弹层;滚动交给首页 .screen,引导时锁住。 */ (function () { 'use strict'; const KEY = 'mmbox.coach.v1'; const FORCE = /[?&]coach=1\b/.test(location.search); try { if (!FORCE && localStorage.getItem(KEY)) return; } catch (e) { return; } const phone = document.getElementById('phone'); const scr = document.getElementById('screen-home'); if (!phone || !scr) return; /* 样式随脚本走(不动 mama-box.css)。z-index 140:高于 modal(125) / pk-sheet(131)。 */ const st = document.createElement('style'); st.textContent = '.mb-coach{position:absolute;inset:0;z-index:140;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s;-webkit-tap-highlight-color:transparent}' + '.mb-coach.show{opacity:1;visibility:visible}' + '.mbc-hole{position:absolute;border-radius:18px;box-shadow:0 0 0 2px rgba(255,255,255,.9),0 0 0 9999px rgba(40,22,16,.62);transition:left .3s,top .3s,width .3s,height .3s;pointer-events:none}' + '.mb-coach.is-moving .mbc-hole,.mb-coach.is-moving .mbc-card{transition:none}' + '.mbc-card{position:absolute;left:20px;right:20px;background:#fff;border-radius:18px;padding:16px 18px 12px;box-shadow:0 12px 32px rgba(0,0,0,.18);transition:top .3s}' + '.mbc-n{display:block;font-size:12px;color:#B08A7C;margin-bottom:4px}' + '.mbc-t{display:block;font-size:17px;color:var(--ink,#2B2320);margin-bottom:6px}' + '.mbc-d{margin:0;font-size:14px;line-height:1.6;color:#6B5A53}' + '.mbc-act{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}' + '.mbc-act button{min-height:40px;min-width:64px;padding:0 16px;border:0;border-radius:20px;font-size:14px;font-weight:600}' + '.mbc-skip{background:none;color:#9C8A83}' + '.mbc-next{background:var(--brand,#F4593C);color:#fff}' + '.screen.mbc-lock{overflow:hidden!important}' + '@media (prefers-reduced-motion:reduce){.mbc-hole,.mbc-card,.mb-coach{transition:none}}'; document.head.appendChild(st); const STEPS = [ { sel: '.hero-pane', t: '这是你的孕周', d: '孕几周、离见面还有几天、宝宝现在有多大,每天打开都会更新。' }, { sel: '.tool-dock', t: '常用孕育工具', d: '数胎动、孕检日历、体重这些常用工具都在这里,点卡片直接进。' }, /* 11-03「此刻」换成测一测 · 科普 · 计算器(js/kit.js);?kit=0 时 #kit 不显示,自动跳过 */ { sel: '#kit', t: '测一测 · 科普 · 计算器', d: '按你的孕周,每天推荐一个小测试、一篇科普和实用计算器。' }, { sel: '.tab-fab', t: '记一下', d: '想记点什么、找更多工具,点中间这颗按钮。' }, { sel: '.tabbar [data-tab="me"]', t: '我的', d: '孕期档案、头像、兜豆和兑换记录都在这里。' } ]; const visible = el => { if (!el) return false; const r = el.getBoundingClientRect(); if (!r.width || !r.height) return false; const cs = getComputedStyle(el); return cs.display !== 'none' && cs.visibility !== 'hidden' && !el.classList.contains('hide'); }; // 有别的弹层 / 不在首页 / 页面在后台 → 先别讲 function busy() { if (document.hidden || !scr.classList.contains('is-active')) return true; if (document.querySelector('.modal.show,.sheet.show,.detail.show,.pk-sheet.show,.search-layer.show,.bx-reveal.show')) return true; const wh = document.getElementById('whPage'); if (wh && wh.getAttribute('aria-hidden') === 'false') return true; return false; } let root, hole, card, idx = -1, on = false, timer = 0; function build() { root = document.createElement('div'); root.className = 'mb-coach'; root.setAttribute('role', 'dialog'); root.setAttribute('aria-modal', 'true'); root.setAttribute('aria-label', '首页使用引导'); root.innerHTML = '' + '

' + '
'; phone.appendChild(root); hole = root.querySelector('.mbc-hole'); card = root.querySelector('.mbc-card'); root.querySelector('.mbc-skip').addEventListener('click', e => { e.stopPropagation(); done(); }); root.querySelector('.mbc-next').addEventListener('click', e => { e.stopPropagation(); go(idx + 1); }); // 点遮罩任意处也算下一步(大靶子);引导时不让页面跟着滑 root.addEventListener('click', () => go(idx + 1)); root.addEventListener('touchmove', e => { if (e.cancelable) e.preventDefault(); }, { passive: false }); root.addEventListener('wheel', e => e.preventDefault(), { passive: false }); } function place() { const s = STEPS[idx], el = s && document.querySelector(s.sel); if (!el || !visible(el)) return false; const p = phone.getBoundingClientRect(), r = el.getBoundingClientRect(), pad = 6; const x = r.left - p.left - pad, y = r.top - p.top - pad, w = r.width + pad * 2, h = r.height + pad * 2; Object.assign(hole.style, { left: x + 'px', top: y + 'px', width: w + 'px', height: h + 'px' }); hole.style.borderRadius = getComputedStyle(el).borderRadius === '0px' ? '16px' : 'calc(' + getComputedStyle(el).borderRadius + ' + 6px)'; const live = STEPS.filter(t => visible(document.querySelector(t.sel))); root.querySelector('.mbc-n').textContent = (live.indexOf(s) + 1) + ' / ' + live.length; root.querySelector('.mbc-t').textContent = s.t; root.querySelector('.mbc-d').textContent = s.d; root.querySelector('.mbc-next').textContent = live.indexOf(s) === live.length - 1 ? '知道了' : '下一步'; // 卡片放在目标下方,放不下就放上方 const ch = card.offsetHeight || 140, below = y + h + 12, ph = p.height; card.style.top = (below + ch < ph - 16 ? below : Math.max(16, y - ch - 12)) + 'px'; return true; } function go(i) { while (i < STEPS.length) { const el = document.querySelector(STEPS[i].sel); if (visible(el)) break; i++; } if (i >= STEPS.length) return done(); idx = i; const el = document.querySelector(STEPS[i].sel); // 首页里的目标先滚到视口中间(底栏不在 .screen 里,不用滚) if (scr.contains(el)) { const sr = scr.getBoundingClientRect(), r = el.getBoundingClientRect(); const want = scr.scrollTop + (r.top - sr.top) - Math.max(80, (sr.height - r.height) / 2 - 60); scr.scrollTop = Math.max(0, want); } root.classList.add('is-moving'); requestAnimationFrame(() => requestAnimationFrame(() => { place(); root.classList.remove('is-moving'); })); } function show() { if (!root) build(); on = true; root.classList.add('show'); scr.classList.add('mbc-lock'); go(idx < 0 ? 0 : idx); } function hideTmp() { if (root) root.classList.remove('show'); scr.classList.remove('mbc-lock'); on = false; } function done() { try { localStorage.setItem(KEY, String(Date.now())); } catch (e) { /* 存不了就只是下次还会出 */ } clearInterval(timer); if (root) { root.classList.remove('show'); setTimeout(() => root.remove(), 300); } scr.classList.remove('mbc-lock'); idx = STEPS.length; } // 每 0.5 秒看一眼:首页安静满 1.5 秒才开讲;讲到一半有弹层冒出来就先收起 let quiet = 0; function tick() { if (idx >= STEPS.length) return; if (busy()) { quiet = 0; if (on) hideTmp(); return; } if (on) return; if ((quiet += 500) >= 1500) show(); } // 等孕周数据回来(头图有真实数字)再开始;最多等 4 秒 let started = false; const start = () => { if (started) return; started = true; timer = setInterval(tick, 500); }; addEventListener('mb-preg', () => setTimeout(start, 600)); setTimeout(start, 4000); addEventListener('resize', () => { if (on) place(); }); })();