diff --git a/index.html b/index.html
index 81cb168..2508e7a 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
兜爸心选 · 孕育陪伴
-
+
@@ -1780,10 +1780,12 @@
+
点一下小星星,听听她们说了什么
+
同孕周悄悄话
diff --git a/js/app.js b/js/app.js
index f731a0d..39160a5 100644
--- a/js/app.js
+++ b/js/app.js
@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
- var VER = '26100174';
+ var VER = '26100175';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3376,7 +3376,7 @@
const x = 36 + ((c + .5 + ((h % 100) / 100 - .5) * .7) / cols) * 58;
const y = 21 + ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .6) / rows) * 38;
const sz = it.mine ? 26 : Math.round(15 + Math.min(it.hugs || 0, 30) / 30 * 11);
- return '';
}).join('');
}
@@ -3407,10 +3407,106 @@
cancelAnimationFrame(m.raf);
m.el.remove();
m.btn.classList.remove('flare', 'gone');
- m.btn.classList.add('seen');
+ m.btn.classList.add('used');
mts.delete(m.i);
if (done) paintCard(m.it, m.i);
+ checkDone();
}
+ /* 十一改(用户:「被点掉的流星划过之后,就不要再出现在原来的位置;点完之后给一些动效、声音」):
+ · 划走的星从夜空里消失(.used),全部落完 → 「星星都落下来啦」+「让星星回来」;
+ · 点下去:一圈光波 + 一把碎星迸开;飞行中头后撒星尘;
+ · 声音用 WebAudio 现合成,不加载音频文件:点星 = 一声随机五声音阶的「叮」(连点几颗像在弹琴),
+ 唰出去 = 一道由低到高的风声;右上角喇叭可关,记在本地。 */
+ function checkDone() {
+ const all = $$('.wsk-st', page);
+ $('#wskDone').classList.toggle('show', all.length > 0 && all.every(b => b.classList.contains('used')) && !mts.size);
+ }
+ const SND = 'mmbox.whsnd.v1';
+ let actx = null, sndOn = true;
+ try { sndOn = localStorage.getItem(SND) !== '0'; } catch (e) {}
+ const sndBtn = $('#whvSnd');
+ const paintSnd = () => { sndBtn.classList.toggle('off', !sndOn); sndBtn.setAttribute('aria-label', sndOn ? '关闭声音' : '打开声音'); };
+ paintSnd();
+ sndBtn.addEventListener('click', () => {
+ sndOn = !sndOn;
+ try { localStorage.setItem(SND, sndOn ? '1' : '0'); } catch (e) {}
+ paintSnd();
+ haptic();
+ if (sndOn) chime();
+ });
+ function ac() {
+ if (!sndOn) return null;
+ try {
+ actx = actx || new (window.AudioContext || window.webkitAudioContext)();
+ if (actx.state === 'suspended') actx.resume();
+ } catch (e) { return null; }
+ return actx;
+ }
+ const NOTES = [1046.5, 1174.7, 1318.5, 1568, 1760, 2093];
+ function chime() {
+ const c = ac();
+ if (!c) return;
+ const t = c.currentTime, f = NOTES[Math.floor(Math.random() * NOTES.length)];
+ const g = c.createGain();
+ g.gain.setValueAtTime(0, t);
+ g.gain.linearRampToValueAtTime(.16, t + .012);
+ g.gain.exponentialRampToValueAtTime(.001, t + 1.8);
+ g.connect(c.destination);
+ [[f, 'sine', 1], [f * 2, 'sine', .28], [f * 3.01, 'triangle', .08]].forEach(([fr, ty, v]) => {
+ const o = c.createOscillator(), og = c.createGain();
+ o.type = ty; o.frequency.value = fr; og.gain.value = v;
+ o.connect(og); og.connect(g); o.start(t); o.stop(t + 1.9);
+ });
+ }
+ function whoosh() {
+ const c = ac();
+ if (!c) return;
+ const t = c.currentTime, d = .9;
+ const buf = c.createBuffer(1, Math.floor(c.sampleRate * d), c.sampleRate), ch = buf.getChannelData(0);
+ for (let k = 0; k < ch.length; k++) ch[k] = Math.random() * 2 - 1;
+ const src = c.createBufferSource(), bp = c.createBiquadFilter(), g = c.createGain();
+ src.buffer = buf;
+ bp.type = 'bandpass'; bp.Q.value = 1.4;
+ bp.frequency.setValueAtTime(500, t);
+ bp.frequency.exponentialRampToValueAtTime(3800, t + d * .75);
+ g.gain.setValueAtTime(0, t);
+ g.gain.linearRampToValueAtTime(.1, t + d * .5);
+ g.gain.exponentialRampToValueAtTime(.001, t + d);
+ src.connect(bp); bp.connect(g); g.connect(c.destination);
+ src.start(t); src.stop(t + d);
+ }
+ function fx(cls, x, y, ms, set) {
+ const e = document.createElement('i');
+ e.className = cls;
+ e.style.left = x.toFixed(1) + 'px';
+ e.style.top = y.toFixed(1) + 'px';
+ if (set) set(e);
+ page.insertBefore(e, card);
+ setTimeout(() => e.remove(), ms);
+ }
+ function tapFx(x, y, gold) {
+ if (RM) return;
+ fx('wfx-ring' + (gold ? ' gold' : ''), x, y, 800);
+ for (let k = 0; k < 10; k++) {
+ const a = Math.PI * 2 * k / 10 + Math.random() * .5, r = 26 + Math.random() * 30;
+ fx('wfx-sp' + (gold ? ' gold' : ''), x, y, 900, e => {
+ e.style.setProperty('--x', (Math.cos(a) * r).toFixed(0) + 'px');
+ e.style.setProperty('--y', (Math.sin(a) * r).toFixed(0) + 'px');
+ e.style.setProperty('--z', (5 + Math.random() * 7).toFixed(0) + 'px');
+ e.style.animationDelay = (Math.random() * 80).toFixed(0) + 'ms';
+ });
+ }
+ }
+ $('#wskBack').addEventListener('click', () => {
+ haptic();
+ seen.clear();
+ $('#wskDone').classList.remove('show');
+ paintSky(list());
+ $('#wskBright').classList.remove('back');
+ void $('#wskBright').offsetWidth;
+ $('#wskBright').classList.add('back');
+ chime();
+ });
const stopAll = () => [...mts.values()].forEach(m => stopMeteor(m));
/* 第一次进来:一闪一闪的提示,点过一次星星就永久收起 */
const TIP = 'mmbox.whtip.v1';
@@ -3432,7 +3528,7 @@
const btn = $('.wsk-st[data-go="' + i + '"]', page);
if (!btn) { paintCard(it, i); return; }
btn.classList.add('cur');
- if (RM) { btn.classList.add('seen'); paintCard(it, i); return; }
+ if (RM) { btn.classList.add('used'); paintCard(it, i); checkDone(); return; }
const pr = page.getBoundingClientRect(), br = btn.getBoundingClientRect();
const x0 = br.left + br.width / 2 - pr.left, y0 = br.top + br.height / 2 - pr.top;
/* 瞄准左下(屏幕左边外、六成高处):越靠左起步角度越陡,保证在屏幕里拖得够长 */
@@ -3457,6 +3553,11 @@
/* 节奏:星闪 420ms → 以约 90px/s 起步并缓缓提速,整句拖出来后再留 0.7 秒给眼睛 → 加速度猛增,1.3 秒内唰出屏幕 */
const T0 = 420, v0 = .09, TR = (body + 20) / v0 + 700, A = .0018, TE = 1300;
btn.classList.add('flare');
+ btn.classList.remove('used');
+ $('#wskDone').classList.remove('show');
+ chime();
+ tapFx(x0, y0, it.mine);
+ let swish = false, dustT = 0;
const t0 = performance.now();
const frame = now => {
const t = now - t0;
@@ -3465,6 +3566,7 @@
const tc = t - T0, dt = Math.max(0, tc - TR), u = Math.min(1, dt / TE);
const s = v0 * tc + v0 * tc * tc / 16000 + .5 * A * dt * dt;
const v = v0 * (1 + tc / 8000) + A * dt;
+ if (dt > 0 && !swish) { swish = true; whoosh(); }
const hx = x0 + dx * s, hy = y0 + dy * s, at = 'translate(' + hx.toFixed(1) + 'px,' + hy.toFixed(1) + 'px) rotate(' + rot.toFixed(1) + 'deg)';
const st = Math.min(1.6, Math.max(0, (v - .25) / .5));
head.style.transform = at + ' scaleX(' + (1 + st * 1.6).toFixed(2) + ')';
@@ -3479,6 +3581,13 @@
c.style.opacity = (Math.min(1, d / 26) * fade).toFixed(2);
c.style.transform = 'translate(' + (x0 + dx * d).toFixed(1) + 'px,' + (y0 + dy * d).toFixed(1) + 'px) translate(-50%,-50%) rotate(' + rot.toFixed(1) + 'deg)';
});
+ if (t - dustT > 70 && u < .7 && hx > -20 && hy < pr.height + 20) {
+ dustT = t;
+ fx('wfx-dust' + (it.mine ? ' gold' : ''), hx + (Math.random() - .5) * 10, hy + (Math.random() - .5) * 10, 1000, e => {
+ e.style.setProperty('--z', (3 + Math.random() * 5).toFixed(0) + 'px');
+ e.style.setProperty('--y', (8 + Math.random() * 14).toFixed(0) + 'px');
+ });
+ }
if (u >= 1) { stopMeteor(m, true); return; }
m.raf = requestAnimationFrame(frame);
};
@@ -3525,6 +3634,7 @@
card.classList.remove('show');
paintPage();
tip(true);
+ checkDone();
page.classList.add('show');
page.setAttribute('aria-hidden', 'false');
drawer(false);
diff --git a/mama-box.css b/mama-box.css
index b677b72..3eabffb 100644
--- a/mama-box.css
+++ b/mama-box.css
@@ -4000,3 +4000,33 @@ a.skip:focus{left:16px;top:16px}
.wmt-l::before{background:linear-gradient(90deg,#fff,rgba(255,255,255,.75) 25%,rgba(245,238,255,.4) 60%,rgba(255,255,255,.08) 92%,transparent)}
.wmt span{text-shadow:0 0 1px rgba(60,30,110,.95),0 0 3px rgba(60,30,110,.7),0 0 12px rgba(255,255,255,.85),0 0 24px rgba(255,255,255,.5)}
.wmt.mine span{text-shadow:0 0 1px rgba(110,60,20,.9),0 0 3px rgba(110,60,20,.6),0 0 12px rgba(255,236,200,.85),0 0 24px rgba(255,210,150,.5)}
+
+/* 十一改:划走的星消失;点星光波 + 碎星;飞行星尘;声音开关;全部落完 */
+.wsk-st.used{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .6s,visibility .6s}
+.wsk-st.flare,.wsk-st.flare.used{visibility:visible;opacity:1}
+.wsk-st.flare.gone{opacity:0}
+.wsk-bright.back .wsk-st{animation:wskBack .9s cubic-bezier(.2,.8,.2,1) both}
+@keyframes wskBack{from{opacity:0;transform:translateY(-30px) scale(.3)}to{opacity:1;transform:none}}
+.wfx-ring,.wfx-sp,.wfx-dust{position:absolute;z-index:2;pointer-events:none}
+.wfx-ring{width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;border:2px solid rgba(255,255,255,.9);
+ box-shadow:0 0 12px rgba(255,255,255,.8),inset 0 0 8px rgba(255,255,255,.6);animation:wfxRing .8s cubic-bezier(.2,.8,.2,1) forwards}
+.wfx-ring.gold{border-color:rgba(255,220,160,.95);box-shadow:0 0 12px rgba(255,200,120,.8),inset 0 0 8px rgba(255,200,120,.6)}
+@keyframes wfxRing{0%{transform:scale(.4);opacity:1}100%{transform:scale(6);opacity:0;border-width:.5px}}
+.wfx-sp{width:var(--z);height:var(--z);margin:calc(var(--z) / -2) 0 0 calc(var(--z) / -2);background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23FFF4E6' stop-opacity='.85'/%3E%3Cstop offset='1' stop-color='%23FFF4E6' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='10' cy='10' r='10' fill='url(%23g)' opacity='.45'/%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFFFFF'/%3E%3C/svg%3E") center/contain no-repeat;
+ filter:drop-shadow(0 0 3px #fff);animation:wfxSp .85s cubic-bezier(.15,.7,.3,1) both}
+.wfx-sp.gold,.wfx-dust.gold{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23FFC27A' stop-opacity='.85'/%3E%3Cstop offset='1' stop-color='%23FFC27A' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='10' cy='10' r='10' fill='url(%23g)' opacity='.45'/%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFE2A8'/%3E%3C/svg%3E")}
+@keyframes wfxSp{0%{transform:translate(0,0) scale(.3) rotate(0);opacity:1}70%{opacity:1}100%{transform:translate(var(--x),var(--y)) scale(1) rotate(90deg);opacity:0}}
+.wfx-dust{width:var(--z);height:var(--z);margin:calc(var(--z) / -2) 0 0 calc(var(--z) / -2);background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23FFF4E6' stop-opacity='.85'/%3E%3Cstop offset='1' stop-color='%23FFF4E6' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='10' cy='10' r='10' fill='url(%23g)' opacity='.45'/%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFFFFF'/%3E%3C/svg%3E") center/contain no-repeat;animation:wfxDust 1s ease-out forwards}
+@keyframes wfxDust{0%{opacity:.95;transform:scale(1)}100%{opacity:0;transform:translateY(var(--y)) scale(.3) rotate(60deg)}}
+.whv-snd{display:grid;place-items:center;width:32px;height:32px;margin-left:auto;border-radius:50%;background:rgba(255,255,255,.12);color:#fff;backdrop-filter:blur(6px)}
+.whv-snd .off,.whv-snd.off .on{display:none}
+.whv-snd.off .off{display:inline}
+.whv-snd.off{color:rgba(255,255,255,.55)}
+.whv-snd+.whv-all{margin-left:8px}
+.wsk-done{position:absolute;left:0;right:0;top:34%;z-index:3;text-align:center;opacity:0;visibility:hidden;transform:translateY(10px);transition:opacity .6s,transform .6s,visibility .6s}
+.wsk-done.show{opacity:1;visibility:visible;transform:none}
+.wsk-done p{margin:0;font-size:19px;font-weight:300;letter-spacing:2px;line-height:1.7;color:#FFF3E2;text-shadow:0 0 12px rgba(255,220,180,.6)}
+.wsk-done p span{font-size:13px;color:rgba(255,255,255,.6)}
+.wsk-done button{margin-top:16px;height:34px;padding:0 18px;border-radius:99px;font-size:13px;font-weight:600;color:#fff;
+ background:rgba(255,255,255,.12);box-shadow:inset 0 0 0 1px rgba(255,255,255,.3),0 0 18px rgba(255,230,200,.25)}
+@media (prefers-reduced-motion:reduce){.wsk-bright.back .wsk-st{animation:none}}