悄悄话流星六改:头窄尾宽光楔、字由小到大、亮头、起步更快后段加速 (v26100170)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 07:21:32 +00:00
parent e45306f0dd
commit 7b174623d3
3 changed files with 48 additions and 22 deletions

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26100169 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26100170 缓存版本标记:改 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 第四节) -->

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26100169';
var VER = '26100170';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3370,7 +3370,7 @@
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 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 ? 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' : '') +
@@ -3421,37 +3421,47 @@
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 th = Math.max(24, Math.min(55, Math.atan2(pr.height * .8 - y0, x0 + 30) * 180 / Math.PI)) * Math.PI / 180, dx = -Math.cos(th), dy = Math.sin(th), rot = -th * 180 / Math.PI;
/* 六改(用户:「头是窄的,往后越来越宽;前面的字小,后面慢慢变大,形成流星尾巴;头特别亮往前滑;
点完亮一下就开始滑,一点点变快,不要太慢也别太快」):
字号从头到尾 12px → 26px 渐大,尾巴是一条从头部尖起、往后张开的光楔;
运动 = 起步就有速度、缓缓提速,整句出来后再猛加速唰出去。 */
const chars = [...String(it.text)], n = chars.length;
const cw = n > 16 ? 21 : 25, gap = 20, body = gap + n * cw;
const f0 = 12, f1 = n > 20 ? 21 : n > 12 ? 24 : 27, gap = 22;
const fs = chars.map((c, k) => f0 + (f1 - f0) * Math.pow(n > 1 ? k / (n - 1) : 1, .85));
const off = [];
fs.reduce((d, f) => { off.push(d + f * .55); return d + f * 1.08; }, gap);
const body = off.length ? off[n - 1] + fs[n - 1] * .6 : gap;
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('');
el.innerHTML = '<i class="wmt-l"></i><i class="wmt-c"></i><i class="wmt-h"></i>' + chars.map((c, k) => '<span style="font-size:' + fs[k].toFixed(1) + 'px">' + 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);
const line = el.children[0], core = el.children[1], head = el.children[2], sp = $$('span', el);
line.style.height = (f1 * 1.9).toFixed(0) + 'px';
line.style.marginTop = (-f1 * .95).toFixed(0) + 'px';
/* 节奏:星闪 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');
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');
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;
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) + ')';
const tl = Math.min(s, body + 70 + 260 * st);
line.style.width = core.style.width = tl.toFixed(0) + 'px';
line.style.transform = core.style.transform = at;
const fade = u < .6 ? 1 : Math.max(0, 1 - (u - .6) / .4);
el.style.setProperty('--bl', (st * 1.6).toFixed(1) + 'px');
sp.forEach((c, k) => {
const d = s - gap - k * cw;
const d = s - off[k];
if (d < 0) { c.style.opacity = 0; return; }
c.style.opacity = (Math.min(1, d / 22) * fade).toFixed(2);
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 (u >= 1) { stopMeteor(); return; }

View File

@@ -3922,3 +3922,19 @@ a.skip:focus{left:16px;top:16px}
@media (prefers-reduced-motion:reduce){
.wsk-deco i,.wsk-st i,.wsk-shoot{animation:none}
}
/* 六改:头窄尾宽的光楔 + 特别亮的头 */
.wmt-l,.wmt-c{position:absolute;left:0;top:0;transform-origin:0 50%;border-radius:0;will-change:transform,width}
.wmt-l{clip-path:polygon(0 50%,100% 0,100% 100%);filter:blur(5px);
background:linear-gradient(90deg,rgba(255,255,255,.75),rgba(220,200,255,.38) 30%,rgba(255,180,200,.16) 70%,transparent)}
.wmt-c{height:6px;margin-top:-3px;clip-path:polygon(0 50%,100% 0,100% 100%);filter:blur(1px);
background:linear-gradient(90deg,#fff,rgba(255,255,255,.55) 35%,transparent)}
.wmt-h{width:16px;height:16px;margin:-8px 0 0 -8px;background:radial-gradient(circle,#fff 45%,rgba(255,255,255,.85) 60%,rgba(255,255,255,0) 72%);
box-shadow:0 0 12px 6px #fff,0 0 34px 16px rgba(230,215,255,.85),0 0 80px 30px rgba(255,190,210,.45)}
.wmt-h::before,.wmt-h::after{content:"";position:absolute;left:50%;top:50%;width:70px;height:2px;margin:-1px 0 0 -35px;border-radius:2px;
background:linear-gradient(90deg,transparent,#fff,transparent)}
.wmt-h::after{transform:rotate(90deg);width:40px;margin-left:-20px}
.wmt span{font-weight:900}
.wmt.mine .wmt-l{background:linear-gradient(90deg,rgba(255,240,210,.8),rgba(255,200,130,.4) 30%,rgba(255,150,90,.16) 70%,transparent)}
.wsk-st.flare i{animation:wskFlare2 .42s ease-out forwards}
@keyframes wskFlare2{0%{transform:scale(1)}55%{transform:scale(3.4);box-shadow:0 0 26px 14px #fff,0 0 60px 24px rgba(230,210,255,.7)}100%{transform:scale(2);box-shadow:0 0 20px 10px #fff}}
.wsk-st.gone{opacity:0;transition:opacity .5s}