悄悄话全屏页五改:流星夜空,点星变流星、字做尾巴先慢后唰、底部内容卡 (v26100169)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
19
index.html
19
index.html
@@ -7,7 +7,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>兜爸心选 · 孕育陪伴</title>
|
||||
<!-- V:26100168 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26100169 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<link rel="stylesheet" href="mama-box.css">
|
||||
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
|
||||
@@ -1766,19 +1766,24 @@
|
||||
层级 z 110:盖住 tabbar(60),低于说一句弹窗(modal 120+),所以在这页里点「说一句」弹窗能盖在上面。 -->
|
||||
<section class="wh-page" id="whPage" role="dialog" aria-modal="true" aria-labelledby="whpTitle" aria-hidden="true">
|
||||
<div class="whp-sky" aria-hidden="true"><i class="s1"></i><i class="s2"></i><i class="s3"></i><i class="moon"></i></div>
|
||||
<!-- 2026-10-02 四改(用户:「色调可以,整体设计再调整,看其他孕妈的内容要做得炫酷一点」):
|
||||
改成「一屏一句」的竖滑沉浸式(像刷短视频):封面 = 大标题 + 今晚星座;往上滑每屏一句大字,
|
||||
逐字亮起;背后两团光晕随每句换色;右侧抱抱大按钮,双击屏幕也能抱抱(爱心在手指处炸开);
|
||||
顶部一排进度条;右上「全部」拉出清单抽屉,点哪句跳哪句。配色沿用深夜星河。 -->
|
||||
<!-- 2026-10-02 五改(用户:「打开页面夜空里特别多星星;点开一颗,它像流星一样滑落,白色的头往下滑,
|
||||
尾巴就是这句话的字;先保证看清,刚开始慢一点,说完了唰地就快了;同时把对应内容呈现出来」):
|
||||
夜空 = 每句一颗可点的亮星 + 一百多颗装饰小星(偶尔有无字的小流星划过);点亮星 → 流星 + 底部内容卡。 -->
|
||||
<div class="whv-aura" id="whvAura" aria-hidden="true"><i></i><i></i></div>
|
||||
<div class="wsk" id="wsk">
|
||||
<div class="wsk-deco" id="wskStars" aria-hidden="true"></div>
|
||||
<i class="wsk-shoot" aria-hidden="true"></i><i class="wsk-shoot s2" aria-hidden="true"></i>
|
||||
<div class="wsk-bright" id="wskBright" role="group" aria-label="夜空里的星星,每颗是一句悄悄话"></div>
|
||||
</div>
|
||||
<header class="whv-top">
|
||||
<button class="whp-back" id="whpBack" type="button" aria-label="返回">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="whv-bars" id="whvBars" aria-hidden="true"></div>
|
||||
<span class="whp-tag" id="whpTag"></span>
|
||||
<button class="whv-all" id="whvAll" type="button">全部</button>
|
||||
</header>
|
||||
<div class="whv" id="whv"></div>
|
||||
<div class="wsk-title" id="wskTitle"></div>
|
||||
<div class="wsc" id="wsc" aria-live="polite"></div>
|
||||
<h2 class="sr-only" id="whpTitle">同孕周悄悄话</h2>
|
||||
<div class="whd" id="whDrawer" aria-hidden="true">
|
||||
<div class="whd-panel" role="dialog" aria-label="全部悄悄话">
|
||||
|
||||
215
js/app.js
215
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26100168';
|
||||
var VER = '26100169';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -3341,43 +3341,44 @@
|
||||
return '<button class="whp-hug' + (it.hugged ? ' on' : '') + '" type="button" data-hug="' + esc(it.id) + '" aria-label="给她一个抱抱">' +
|
||||
HEART + '<b>' + (it.hugs || '') + '</b></button>';
|
||||
}
|
||||
/* 四改:一屏一句的竖滑沉浸页。slide 0 = 封面(标题 + 星座),slide k = 第 k 句。 */
|
||||
/* 五改:流星夜空。用户原话:「打开页面夜空里特别多星星;点开一颗,它像流星一样滑落……那个头就是白色的,
|
||||
尾巴就是这些字;首先保证用户能看清,刚开始慢一点,说完了慢慢的唰的就快了;同时把对应内容呈现出来」
|
||||
· 亮星 = 一句(data-go = 下标 + 1),其余一百多颗只是装饰;
|
||||
· 点亮星:星闪一下 → 变成白色流星头往左下滑,字从星里一个个拖出来当尾巴;慢速段按字数给够阅读时间,
|
||||
之后加速、拉长、字被拖糊,唰地划出屏幕;星星回来时变暗(看过了);
|
||||
· 底部内容卡同时出现:整句 + 孕周 + 抱抱 + 「下一颗」。流星在卡片下面穿过,不挡字。 */
|
||||
const RM = !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches);
|
||||
const AURA = [['#6B4AA8', '#C2667A'], ['#3F4FA6', '#8E5BB0'], ['#9A4F86', '#E0907A'], ['#365C9E', '#7A58AE'], ['#7D3F8C', '#D27A9A']];
|
||||
const hsh = s => { let h = 7; for (const c of String(s)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return h; };
|
||||
let cur = 0, io = null, vl = [];
|
||||
/* 沉浸页先看别人的,自己说的放最后(金色收尾) */
|
||||
const vlist = () => list().filter(x => !x.mine).concat(list().filter(x => x.mine));
|
||||
const whv = $('#whv');
|
||||
function skySvg(l) {
|
||||
const n = l.length, cols = n > 12 ? 5 : 4, rows = Math.max(2, Math.ceil(n / cols));
|
||||
const pts = l.map((it, i) => {
|
||||
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols), cc = r % 2 ? cols - 1 - c : c;
|
||||
return { i, it, x: ((cc + .5 + ((h % 100) / 100 - .5) * .62) / cols) * 100, y: ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .55) / rows) * 100,
|
||||
sz: it.mine ? 12 : Math.round(5 + Math.min(it.hugs || 0, 40) / 40 * 10) };
|
||||
});
|
||||
return '<svg class="whs-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><polyline points="' +
|
||||
pts.map(p => p.x.toFixed(1) + ',' + p.y.toFixed(1)).join(' ') + '"/></svg>' +
|
||||
pts.map(p => '<button type="button" class="whs-star' + (p.it.mine ? ' mine' : '') + '" data-go="' + (p.i + 1) + '" style="left:' + p.x.toFixed(1) + '%;top:' + p.y.toFixed(1) +
|
||||
'%;--s:' + p.sz + 'px;--tw:' + ((hsh(p.it.id) % 30) / 10).toFixed(1) + 's" aria-label="' + esc(p.it.text) + '"><i></i></button>').join('');
|
||||
const card = $('#wsc');
|
||||
const seen = new Set();
|
||||
let cur = 0, mt = null;
|
||||
function paintDeco() {
|
||||
const box = $('#wskStars');
|
||||
if (box.childElementCount) return;
|
||||
let h = '';
|
||||
for (let k = 0; k < 130; k++) {
|
||||
const r = Math.random(), sz = r < .75 ? 1 + Math.random() : r < .95 ? 2 + Math.random() : 3;
|
||||
h += '<i style="left:' + (Math.random() * 100).toFixed(1) + '%;top:' + (Math.random() * 92).toFixed(1) + '%;width:' + sz.toFixed(1) + 'px;height:' + sz.toFixed(1) +
|
||||
'px;--o:' + (.25 + Math.random() * .6).toFixed(2) + ';animation-delay:-' + (Math.random() * 6).toFixed(1) + 's;animation-duration:' + (3 + Math.random() * 4).toFixed(1) + 's"></i>';
|
||||
}
|
||||
box.innerHTML = h;
|
||||
}
|
||||
function slide(it, i, n) {
|
||||
const t = String(it.text || ''), len = [...t].length;
|
||||
const fs = len <= 9 ? 'xl' : len <= 18 ? 'l' : 'm';
|
||||
const chars = [...t].map((c, k) => '<span style="--c:' + k + '">' + esc(c) + '</span>').join('');
|
||||
const rail = it.mine
|
||||
? '<div class="whv-got">' + HEART + '<b>' + (it.hugs || 0) + '</b><small>收到的抱抱</small></div>'
|
||||
: '<button class="whv-hug whp-hug' + (it.hugged ? ' on' : '') + '" type="button" data-hug="' + esc(it.id) + '" aria-label="给她一个抱抱">' + HEART + '<b>' + (it.hugs || '') + '</b></button><small class="whv-hl">抱抱</small>';
|
||||
return '<article class="whv-s' + (it.mine ? ' mine' : '') + '" data-i="' + (i + 1) + '">' +
|
||||
'<div class="whv-in">' +
|
||||
'<div class="whv-no"><b>' + String(i + 1).padStart(2, '0') + '</b><span>/ ' + String(n).padStart(2, '0') + '</span>' + (it.mine ? '<em>我说的</em>' : '') + '</div>' +
|
||||
'<p class="whv-q ' + fs + '">' + chars + '</p>' +
|
||||
'<div class="whv-meta">孕 ' + it.week + ' 周的妈妈 · ' + esc(it.ago) + '</div>' +
|
||||
(i === 0 && !it.mine ? '<div class="whv-tip">双击屏幕,也能给她一个抱抱</div>' : '') +
|
||||
'</div><div class="whv-rail">' + rail + '</div></article>';
|
||||
/* 亮星落在夜空中上部偏右(流星往左下走,右边起步才划得够长);抖动网格,不会叠在一起 */
|
||||
function paintSky(l) {
|
||||
const n = l.length, cols = n > 16 ? 5 : 4, rows = Math.max(2, Math.ceil(n / cols));
|
||||
const fresh = l.findIndex(x => !x.mine && !seen.has(x.id));
|
||||
$('#wskBright').innerHTML = l.map((it, i) => {
|
||||
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols);
|
||||
const x = 24 + ((c + .5 + ((h % 100) / 100 - .5) * .7) / cols) * 70;
|
||||
const y = 21 + ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .6) / rows) * 38;
|
||||
const sz = it.mine ? 9 : Math.round(4 + Math.min(it.hugs || 0, 30) / 30 * 6);
|
||||
return '<button type="button" class="wsk-st' + (it.mine ? ' mine' : '') + (seen.has(it.id) ? ' seen' : '') + (i === fresh && !seen.size ? ' hint' : '') +
|
||||
'" data-go="' + (i + 1) + '" style="left:' + x.toFixed(1) + '%;top:' + y.toFixed(1) + '%;--s:' + sz + 'px;--tw:' + (2 + (h % 30) / 10).toFixed(1) + 's" aria-label="' + esc(it.text) + '"><i></i></button>';
|
||||
}).join('');
|
||||
}
|
||||
function paintList(l) {
|
||||
$('#whpCount').textContent = l.length + ' 句 · 我说的在最后';
|
||||
$('#whpCount').textContent = l.length + ' 句 · 最新在上';
|
||||
$('#whpList').innerHTML = l.map((it, i) => {
|
||||
const lv = it.mine ? 'mine' : it.hugs >= 25 ? 'l3' : it.hugs >= 10 ? 'l2' : 'l1';
|
||||
return '<div class="whl ' + lv + '"><button type="button" class="whl-t" data-go="' + (i + 1) + '">' +
|
||||
@@ -3385,46 +3386,97 @@
|
||||
'<div class="whp-meta"><span>孕 ' + it.week + ' 周 · ' + esc(it.ago) + '</span>' + hugBtn(it) + '</div></div>';
|
||||
}).join('');
|
||||
}
|
||||
function setCur(i) {
|
||||
cur = i;
|
||||
const n = list().length;
|
||||
$$('.whv-bars i', page).forEach((b, k) => { b.classList.toggle('done', k < i - 1); b.classList.toggle('on', k === i - 1); });
|
||||
$('#whvBars').classList.toggle('hide', i === 0);
|
||||
const it = vl[i - 1];
|
||||
const pal = it && it.mine ? ['#A8723A', '#E0A86A'] : i === 0 ? ['#5A3B72', '#4A2C55'] : AURA[(i - 1) % AURA.length];
|
||||
function paintCard(it, i) {
|
||||
card.innerHTML = '<p class="wsc-q">' + (it.mine ? '<em>我说的</em>' : '') + esc(it.text) + '</p>' +
|
||||
'<div class="wsc-m"><span>孕 ' + it.week + ' 周的妈妈 · ' + esc(it.ago) + '</span>' + hugBtn(it) + '</div>' +
|
||||
'<button type="button" class="wsc-next" data-next="1">下一颗 <svg viewBox="0 0 24 24" width="13" height="13"><path d="M9 5l7 7-7 7" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg></button>';
|
||||
card.classList.remove('show');
|
||||
void card.offsetWidth;
|
||||
card.classList.add('show');
|
||||
const pal = it.mine ? ['#A8723A', '#E0A86A'] : AURA[(i - 1) % AURA.length];
|
||||
const au = $$('#whvAura i');
|
||||
au[0].style.backgroundColor = pal[0]; au[1].style.backgroundColor = pal[1];
|
||||
$$('.whs-star', page).forEach(s => s.classList.toggle('on', +s.dataset.go === i));
|
||||
}
|
||||
function stopMeteor() {
|
||||
if (!mt) return;
|
||||
cancelAnimationFrame(mt.raf);
|
||||
mt.el.remove();
|
||||
mt.btn.classList.remove('flare', 'gone');
|
||||
mt.btn.classList.add('seen');
|
||||
mt = null;
|
||||
}
|
||||
function launch(i) {
|
||||
const it = list()[i - 1];
|
||||
if (!it) return;
|
||||
stopMeteor();
|
||||
cur = i;
|
||||
seen.add(it.id);
|
||||
$$('.wsk-st.hint', page).forEach(x => x.classList.remove('hint'));
|
||||
$$('.wsk-st.cur', page).forEach(x => x.classList.remove('cur'));
|
||||
paintCard(it, i);
|
||||
const btn = $('.wsk-st[data-go="' + i + '"]', page);
|
||||
if (!btn) return;
|
||||
btn.classList.add('cur');
|
||||
if (RM) { btn.classList.add('seen'); 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;
|
||||
/* 瞄准左下(屏幕左边外、六成高处):越靠左起步角度越陡,保证在屏幕里拖得够长 */
|
||||
const th = Math.max(22, Math.min(50, Math.atan2(pr.height * .62 - y0, x0 + 30) * 180 / Math.PI)) * Math.PI / 180, dx = -Math.cos(th), dy = Math.sin(th), rot = -th * 180 / Math.PI;
|
||||
const chars = [...String(it.text)], n = chars.length;
|
||||
const cw = n > 16 ? 21 : 25, gap = 20, body = gap + n * cw;
|
||||
const el = document.createElement('div');
|
||||
el.className = 'wmt' + (it.mine ? ' mine' : '');
|
||||
el.style.setProperty('--fs', (cw - 4) + 'px');
|
||||
el.innerHTML = '<i class="wmt-l"></i><i class="wmt-h"></i>' + chars.map(c => '<span>' + esc(c) + '</span>').join('');
|
||||
page.insertBefore(el, card);
|
||||
const line = el.children[0], head = el.children[1], sp = $$('span', el);
|
||||
/* 节奏:星闪 380ms → 慢飘(按字数给阅读时间,起步再缓一点)→ 750ms 加速唰出去 */
|
||||
const T0 = 380, T1 = Math.min(11000, 2400 + n * 300), T2 = 800, L1 = body + 60, v1 = L1 / (.94 * T1);
|
||||
btn.classList.add('flare');
|
||||
const t0 = performance.now();
|
||||
const frame = now => {
|
||||
const t = now - t0;
|
||||
if (t < T0) { mt.raf = requestAnimationFrame(frame); return; }
|
||||
btn.classList.add('gone');
|
||||
const t1 = t - T0;
|
||||
let s, u = 0;
|
||||
if (t1 < T1) { const p = t1 / T1; s = L1 * (p < .12 ? p * p / .24 : p - .06) / .94; }
|
||||
else { u = Math.min(1, (t1 - T1) / T2); s = L1 + v1 * T2 * u + 1600 * Math.pow(u, 2.6); }
|
||||
const hx = x0 + dx * s, hy = y0 + dy * s;
|
||||
head.style.transform = 'translate(' + hx.toFixed(1) + 'px,' + hy.toFixed(1) + 'px) rotate(' + rot.toFixed(1) + 'deg) scaleX(' + (1 + u * 5).toFixed(2) + ')';
|
||||
line.style.width = Math.min(s, body + 50 + 700 * u * u).toFixed(0) + 'px';
|
||||
line.style.transform = 'translate(' + hx.toFixed(1) + 'px,' + hy.toFixed(1) + 'px) rotate(' + rot.toFixed(1) + 'deg)';
|
||||
const fade = Math.max(0, 1 - u * 1.25);
|
||||
el.style.setProperty('--bl', (u * 5).toFixed(1) + 'px');
|
||||
sp.forEach((c, k) => {
|
||||
const d = s - gap - k * cw;
|
||||
if (d < 0) { c.style.opacity = 0; return; }
|
||||
c.style.opacity = (Math.min(1, d / 22) * 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 (u >= 1) { stopMeteor(); return; }
|
||||
mt.raf = requestAnimationFrame(frame);
|
||||
};
|
||||
mt = { el, btn, raf: requestAnimationFrame(frame) };
|
||||
}
|
||||
function nextStar() {
|
||||
const l = list(), n = l.length;
|
||||
for (let k = 1; k <= n; k++) {
|
||||
const j = (cur - 1 + k) % n;
|
||||
if (!seen.has(l[j].id) && !l[j].mine) return j + 1;
|
||||
}
|
||||
return cur % n + 1;
|
||||
}
|
||||
function paintPage() {
|
||||
const l = vl = vlist(), n = l.length;
|
||||
const l = list(), n = l.length;
|
||||
const bh = (new Date().getUTCHours() + 8) % 24, night = bh >= 18 || bh < 5;
|
||||
whv.innerHTML =
|
||||
'<article class="whv-s whv-cover" data-i="0"><div class="whv-in">' +
|
||||
'<span class="whp-tag">' + (data ? '孕 ' + data.week + ' 周 · 前后一周的妈妈' : '') + '</span>' +
|
||||
'<div class="whv-ct">' + (night ? '今晚' : '今天') + ',<br><b>' + n + '</b> 句悄悄话</div>' +
|
||||
'<p class="whp-sub">不留名字,只留一句话。听到了,就给她一个抱抱。</p>' +
|
||||
'<div class="whp-sky2" id="whpSky" role="group" aria-label="今晚的星星,每颗是一句悄悄话">' + skySvg(l) + '</div>' +
|
||||
'<div class="whv-down" aria-hidden="true"><i></i>上滑,听她们说</div></div></article>' +
|
||||
l.map((it, i) => slide(it, i, n)).join('') +
|
||||
'<article class="whv-s whv-end" data-i="' + (n + 1) + '"><div class="whv-in"><div class="whv-ct sm">' +
|
||||
(night ? '都在这里了,<br>晚安。' : '都在这里了。') + '</div><p class="whp-sub">你也说一句吧,她们会听到的。</p></div></article>';
|
||||
$('#whvBars').innerHTML = l.map(() => '<i></i>').join('');
|
||||
$('#whpTag').textContent = data ? '孕 ' + data.week + ' 周 · 前后一周的妈妈' : '';
|
||||
$('#wskTitle').innerHTML = '<div class="wsk-t">' + (night ? '今晚' : '今天') + ',<b>' + n + '</b> 句悄悄话</div><p>每颗亮星是一句。点一颗,听她说。</p>';
|
||||
paintDeco();
|
||||
paintSky(l);
|
||||
paintList(l);
|
||||
if (cur && list()[cur - 1]) $$('.wsk-st[data-go="' + cur + '"]', page).forEach(x => x.classList.add('cur'));
|
||||
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' : data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
|
||||
if (io) io.disconnect();
|
||||
if ('IntersectionObserver' in window) {
|
||||
io = new IntersectionObserver(es => es.forEach(e => {
|
||||
e.target.classList.toggle('on', e.isIntersecting);
|
||||
if (e.isIntersecting) setCur(+e.target.dataset.i);
|
||||
}), { root: whv, threshold: .6 });
|
||||
$$('.whv-s', whv).forEach(s => io.observe(s));
|
||||
} else $$('.whv-s', whv).forEach(s => s.classList.add('on'));
|
||||
setCur(0);
|
||||
}
|
||||
function goTo(i, smooth) {
|
||||
const s = $('.whv-s[data-i="' + i + '"]', whv);
|
||||
if (s) whv.scrollTo({ top: s.offsetTop, behavior: smooth && !RM ? 'smooth' : 'auto' });
|
||||
}
|
||||
function drawer(on) {
|
||||
$('#whDrawer').classList.toggle('show', on);
|
||||
@@ -3434,43 +3486,24 @@
|
||||
$('#whdX').addEventListener('click', () => drawer(false));
|
||||
$('#whDrawer').addEventListener('click', e => { if (e.target.id === 'whDrawer') drawer(false); });
|
||||
page.addEventListener('click', e => {
|
||||
const g = e.target.closest('[data-go]');
|
||||
const g = e.target.closest('[data-go],[data-next]');
|
||||
if (!g) return;
|
||||
haptic();
|
||||
drawer(false);
|
||||
goTo(+g.dataset.go, true);
|
||||
});
|
||||
/* 双击屏幕 = 抱抱:大爱心在手指处炸开,再走下面同一条抱抱逻辑 */
|
||||
let lastTap = 0;
|
||||
whv.addEventListener('click', e => {
|
||||
if (e.target.closest('button')) return;
|
||||
const now = Date.now();
|
||||
if (now - lastTap > 320) { lastTap = now; return; }
|
||||
lastTap = 0;
|
||||
const s = e.target.closest('.whv-s');
|
||||
const hb = s && $('.whv-hug', s);
|
||||
if (!hb) return;
|
||||
if (!RM) {
|
||||
const big = document.createElement('i');
|
||||
big.className = 'whv-big';
|
||||
big.innerHTML = HEART;
|
||||
const r = page.getBoundingClientRect();
|
||||
big.style.left = (e.clientX - r.left) + 'px';
|
||||
big.style.top = (e.clientY - r.top) + 'px';
|
||||
page.appendChild(big);
|
||||
setTimeout(() => big.remove(), 900);
|
||||
}
|
||||
if (!hb.classList.contains('on')) hb.click();
|
||||
launch(g.dataset.next ? nextStar() : +g.dataset.go);
|
||||
});
|
||||
function openPage() {
|
||||
stopMeteor();
|
||||
cur = 0;
|
||||
card.classList.remove('show');
|
||||
paintPage();
|
||||
page.classList.add('show');
|
||||
page.setAttribute('aria-hidden', 'false');
|
||||
whv.scrollTop = 0;
|
||||
drawer(false);
|
||||
haptic();
|
||||
}
|
||||
function closePage() {
|
||||
stopMeteor();
|
||||
drawer(false);
|
||||
page.classList.remove('show');
|
||||
page.setAttribute('aria-hidden', 'true');
|
||||
@@ -3591,7 +3624,7 @@
|
||||
paintEntry();
|
||||
paintPage();
|
||||
closeModal();
|
||||
setTimeout(() => goTo(vl.findIndex(x => x.id === it.id) + 1, true), 80);
|
||||
setTimeout(() => launch(1), 350);
|
||||
toast('发出去啦,同孕周妈妈会给你抱抱的');
|
||||
})
|
||||
.catch(() => toast('网络不太好,再试一次'))
|
||||
|
||||
58
mama-box.css
58
mama-box.css
@@ -3864,3 +3864,61 @@ a.skip:focus{left:16px;top:16px}
|
||||
.whv-s.mine .whv-q{background:none;-webkit-background-clip:border-box;background-clip:border-box;color:#FFDDAE;text-shadow:0 0 22px rgba(255,190,120,.55)}
|
||||
.whd-x{display:grid;place-items:center}
|
||||
.whv-ct b{font-size:60px;margin-right:4px}
|
||||
|
||||
/* ===== 2026-10-02 悄悄话全屏页 · 五改:流星夜空 ===== */
|
||||
.wsk{position:absolute;inset:0;z-index:1}
|
||||
.wsk-deco{position:absolute;inset:0;pointer-events:none}
|
||||
.wsk-deco i{position:absolute;border-radius:50%;background:#fff;opacity:var(--o);animation:wskTw 4s ease-in-out infinite alternate}
|
||||
@keyframes wskTw{from{opacity:var(--o)}to{opacity:calc(var(--o) * .25)}}
|
||||
.wsk-deco::before{content:"";position:absolute;left:-20%;right:-20%;top:18%;height:46%;transform:rotate(-24deg);
|
||||
background:radial-gradient(ellipse at center,rgba(210,180,255,.13),rgba(255,190,200,.05) 45%,transparent 70%);filter:blur(10px)}
|
||||
/* 偶尔划过的无字小流星,只是氛围 */
|
||||
.wsk-shoot{position:absolute;top:12%;right:-10%;width:110px;height:1.5px;border-radius:2px;pointer-events:none;opacity:0;
|
||||
background:linear-gradient(90deg,#fff,rgba(255,255,255,0));transform:rotate(-28deg);animation:wskShoot 9s linear infinite 2s}
|
||||
.wsk-shoot.s2{top:34%;right:-20%;width:80px;animation-duration:13s;animation-delay:6.5s}
|
||||
@keyframes wskShoot{0%{opacity:0;translate:0 0}2%{opacity:.9}8%{opacity:0;translate:-320px 170px}100%{opacity:0;translate:-320px 170px}}
|
||||
.wsk-bright{position:absolute;inset:0}
|
||||
.wsk-st{position:absolute;width:40px;height:40px;margin:-20px 0 0 -20px;display:grid;place-items:center;border-radius:50%;transition:opacity .6s}
|
||||
.wsk-st i{display:block;width:var(--s);height:var(--s);border-radius:50%;background:#FFF8EC;
|
||||
box-shadow:0 0 6px 2px rgba(255,240,220,.75),0 0 18px 4px rgba(200,180,255,.35);animation:wskGl var(--tw) ease-in-out infinite alternate}
|
||||
.wsk-st i::before,.wsk-st i::after{content:"";position:absolute;left:50%;top:50%;width:calc(var(--s) * 3.4);height:1px;margin:-.5px 0 0 calc(var(--s) * -1.7);
|
||||
background:linear-gradient(90deg,transparent,rgba(255,255,255,.8),transparent)}
|
||||
.wsk-st i::after{transform:rotate(90deg)}
|
||||
@keyframes wskGl{from{transform:scale(.8);opacity:.75}to{transform:scale(1.1);opacity:1}}
|
||||
.wsk-st.mine i{background:#FFD596;box-shadow:0 0 8px 3px rgba(255,200,120,.85),0 0 22px 6px rgba(255,170,90,.4)}
|
||||
.wsk-st.seen i{opacity:.4;animation:none;box-shadow:0 0 4px 1px rgba(255,240,220,.4)}
|
||||
.wsk-st.seen i::before,.wsk-st.seen i::after{display:none}
|
||||
.wsk-st.hint::after,.wsk-st.cur::after{content:"";position:absolute;inset:4px;border-radius:50%;border:1.5px solid rgba(255,226,190,.75);animation:whRing 2s ease-out infinite}
|
||||
.wsk-st.flare i{animation:wskFlare .38s ease-out forwards;opacity:1}
|
||||
@keyframes wskFlare{0%{transform:scale(1)}60%{transform:scale(2.6);box-shadow:0 0 20px 10px rgba(255,255,255,.9)}100%{transform:scale(1.6)}}
|
||||
.wsk-st.gone{opacity:0}
|
||||
.wsk-title{position:absolute;left:24px;right:24px;top:calc(62px + env(safe-area-inset-top));z-index:2;pointer-events:none}
|
||||
.wsk-t{font-size:26px;font-weight:900;letter-spacing:1px}
|
||||
.wsk-t b{font-size:40px;margin:0 3px;font-family:"DIN Alternate","DIN",Helvetica,Arial,sans-serif;background:linear-gradient(120deg,#FFE2B8,#FF9E8A);-webkit-background-clip:text;background-clip:text;color:transparent}
|
||||
.wsk-title p{margin:6px 0 0;font-size:12.5px;color:rgba(255,255,255,.55);letter-spacing:.5px}
|
||||
.wh-page .whv-top{background:none}
|
||||
.wh-page .whv-top .whp-tag{margin-left:2px}
|
||||
/* 流星:白色头 + 一条渐隐的光尾 + 字一个个拖在尾巴上 */
|
||||
.wmt{position:absolute;inset:0;z-index:2;pointer-events:none;--bl:0px}
|
||||
.wmt-h,.wmt-l,.wmt span{position:absolute;left:0;top:0;will-change:transform}
|
||||
.wmt-h{width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;background:#fff;transform-origin:center;
|
||||
box-shadow:0 0 10px 4px #fff,0 0 30px 12px rgba(220,200,255,.75),0 0 60px 22px rgba(255,180,200,.35)}
|
||||
.wmt-l{height:3px;margin-top:-1.5px;transform-origin:0 50%;border-radius:2px;
|
||||
background:linear-gradient(90deg,rgba(255,255,255,.95),rgba(225,205,255,.5) 25%,rgba(255,190,200,.15) 70%,transparent);filter:blur(.6px)}
|
||||
.wmt span{font-size:var(--fs);font-weight:900;line-height:1;color:#fff;opacity:0;white-space:nowrap;filter:blur(var(--bl));
|
||||
text-shadow:0 0 8px rgba(255,255,255,.7),0 0 18px rgba(210,180,255,.6)}
|
||||
.wmt.mine .wmt-h{background:#FFF2D8;box-shadow:0 0 10px 4px #FFE2B0,0 0 30px 12px rgba(255,200,120,.75),0 0 60px 22px rgba(255,150,90,.35)}
|
||||
.wmt.mine span{color:#FFE4BC;text-shadow:0 0 8px rgba(255,210,150,.8),0 0 18px rgba(255,160,90,.5)}
|
||||
/* 内容卡:流星同时出现,保证整句看得清 */
|
||||
.wsc{position:absolute;left:16px;right:16px;bottom:calc(132px + env(safe-area-inset-bottom));z-index:3;padding:16px 18px 14px;border-radius:20px;
|
||||
background:linear-gradient(140deg,rgba(255,255,255,.13),rgba(255,255,255,.05));box-shadow:inset 0 0 0 1px rgba(255,255,255,.14),0 12px 30px rgba(10,5,30,.35);
|
||||
backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);opacity:0;visibility:hidden;transform:translateY(16px);transition:opacity .4s,transform .45s cubic-bezier(.2,.8,.2,1),visibility .4s}
|
||||
.wsc.show{opacity:1;visibility:visible;transform:none}
|
||||
.wsc-q{margin:0;padding-right:64px;font-size:19px;line-height:1.5;font-weight:800}
|
||||
.wsc-q em{display:inline-block;margin-right:6px;vertical-align:2px;font-style:normal;font-size:11px;font-weight:800;padding:2px 8px;border-radius:99px;background:#FFD596;color:#5A3510}
|
||||
.wsc-m{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:10px;font-size:12px;color:rgba(255,255,255,.55)}
|
||||
.wsc-next{position:absolute;right:12px;top:12px;display:flex;align-items:center;gap:2px;height:28px;padding:0 9px 0 11px;border-radius:99px;
|
||||
background:rgba(255,255,255,.1);color:rgba(255,255,255,.85);font-size:12px;font-weight:700}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.wsk-deco i,.wsk-st i,.wsk-shoot{animation:none}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user