v26110480 踢屏幕统一静止 25~35 秒一次;精彩分享 4~6 篇也出点赞头像(4~6 个随机),白底头像换深色

This commit is contained in:
deploy
2026-11-05 16:37:25 +00:00
parent b20cc204ab
commit aed6d78721
26 changed files with 71 additions and 42 deletions

View File

@@ -83,9 +83,10 @@
/* ---------- 孕周 → 阶段 ---------- */
const STAGES = {
// drum:原地带节奏连踢的概率;chain:踢完不歇、旁边紧接着再踢的概率,最多连 chainMax 下(2026-10-04 用户:「有时会带点节奏、可以连续踢」)
flutter: { order: ['flick', 'flick', 'tap', 'flick'], gap: [10, 18], kick: false, again: 0, double: .45, follow: false, drum: 0, chain: 0 },
active: { order: ORDER, gap: [14, 25], kick: true, again: .34, double: 0, follow: true, drum: .4, chain: .3, chainMax: 3 },
late: { order: ['roll', 'butt', 'foot', 'roll', 'knee'], gap: [14, 25], kick: false, again: .15, double: 0, follow: true, slow: true, drum: .22, chain: .15, chainMax: 2 }
// 11-05 起间隔统一 25~35 秒(gap() 读 GAP,这里各阶段的 gap 只作记录)
flutter: { order: ['flick', 'flick', 'tap', 'flick'], gap: [25, 35], kick: false, again: 0, double: .45, follow: false, drum: 0, chain: 0 },
active: { order: ORDER, gap: [25, 35], kick: true, again: .34, double: 0, follow: true, drum: .4, chain: .3, chainMax: 3 },
late: { order: ['roll', 'butt', 'foot', 'roll', 'knee'], gap: [25, 35], kick: false, again: .15, double: 0, follow: true, slow: true, drum: .22, chain: .15, chainMax: 2 }
};
// 原地连踢的节奏型(每拍间隔,秒):匀速「哒哒哒」、「哒哒—哒」、「哒—哒哒」、两下一组
const RHYTHMS = [[.42, .42, .42], [.3, .3, .75, .3], [.6, .28, .28], [.32, .9, .32], [.38, .38, .38, .38]];
@@ -94,7 +95,12 @@
const stageOf = w => w == null || w < 20 ? null : w < 26 ? STAGES.flutter : w < 34 ? STAGES.active : STAGES.late;
let stage = stageOf(week);
// 两次之间隔多久:20~22 周最稀(再 +40%),越往后越接近本阶段的基准
const gap = () => { const g = stage ? stage.gap : [6, 10], m = week != null && week < 23 ? 1.4 : 1; return (g[0] + Math.random() * (g[1] - g[0])) * m; };
/* 11-05 用户:「用户来到首页静止不动,至少停 25 秒、25~35 秒之间随机才踢一次,所有孕周都这样」→ 各阶段统一 25~35 秒,不再按阶段 / 20~22 周加长;
而且要「一直没动」:点屏幕、滑动都会把倒计时重新从 25~35 秒数起(见 restart() 调用处),回到首页、开关打开也从头数 */
const GAP = [25, 35];
const gap = () => GAP[0] + Math.random() * (GAP[1] - GAP[0]);
let lastInput = -1e9; /* 最近一次真正的手指 / 滚轮操作(图片加载、布局变化引起的 scroll 不算用户动了) */
const restart = () => { if (mode === 'wait') wait = gap(); };
addEventListener('mb-preg', e => {
if (dbgWeek) return;
week = e.detail ? e.detail.week : null; stage = stageOf(week);
@@ -313,7 +319,7 @@
}
scr.addEventListener('pointerdown', e => {
lastTouch = performance.now();
lastTouch = lastInput = performance.now(); restart();
if (isOff() || (mode !== 'show' && !(mode === 'hide' && ft.h.v > 3)) || !ft.catchable) return;
const p = toContent(e.clientX, e.clientY), fx = ft.x + ft.ox, fy = ft.y + ft.oy;
// 只认鼓包中心那一圈;点在边上照常走原来的点击
@@ -329,7 +335,9 @@
window.addEventListener('click', e => {
if (performance.now() < blockUntil) { blockUntil = 0; e.preventDefault(); e.stopImmediatePropagation(); }
}, true);
scr.addEventListener('scroll', () => { lastTouch = performance.now(); run(); }, { passive: true });
/* 手指松开后的惯性滑动(3 秒内)也算在动;上面模块高度变化引起的滚动校正(浏览器 scroll anchoring)不算,否则停在页面中间时一直数不到 */
scr.addEventListener('scroll', () => { const t = performance.now(); if (t - lastInput < 3000) { lastTouch = t; restart(); } run(); }, { passive: true });
['touchstart', 'wheel'].forEach(t => scr.addEventListener(t, () => { lastInput = performance.now(); restart(); }, { passive: true }));
/* ---------- 物理 ---------- */
function step(dt) {
@@ -381,8 +389,8 @@
else if (still < .4 && (still += dt) >= .4) {
const fy = ft.y + ft.oy - top, seen = fy > 30 && fy < scr.clientHeight - 30;
if (!seen && stage && stage.follow) {
if (mode === 'show' || mode === 'hide') { ft.h.v = 0; ft.h.vel = 0; mode = 'wait'; release(); }
if (mode === 'wait') wait = Math.min(wait, 1 + Math.random() * .6);
if (mode === 'show' || mode === 'hide') { ft.h.v = 0; ft.h.vel = 0; mode = 'wait'; release(); wait = gap(); ft.chainNext = false; } /* 11-05 正踢着被滑走:重新等 25~35 秒 */
/* 11-05 起不再把等待缩到 1 秒多(要静止 25~35 秒才踢),只把宝宝挪到这一屏 */
}
}
spring(ft.h, dt); if (ft.h.v < 0) { ft.h.v = 0; ft.h.vel *= -.2; }
@@ -495,9 +503,12 @@
/* ---------- 循环:有东西在动才跑 rAF,静止时只用定时器数「下一次什么时候顶」 ---------- */
let running = false, last = 0, painted = false;
function frame(now) {
const dt = Math.min((now - last) / 1000, 1 / 20); last = now;
const wall = Math.min((now - last) / 1000, 1), dt = Math.min(wall, 1 / 20); last = now;
/* 物理按 dt 封顶防跳帧;等待倒计时按真实时间补齐(掉帧 / 省电模式下 rAF 慢,按封顶的 dt 数会数成一倍长) */
if (mode === 'wait' && active() && idle() && wall > dt) wait -= wall - dt;
if (!active()) {
if (mode !== 'wait') { mode = 'wait'; wait = 2; }
if (mode !== 'wait') mode = 'wait';
wait = gap(); /* 不在首页时一直重置:回到首页后从头数 25~35 秒 */
ft.h.v = ft.h.vel = 0; pk.d.v = pk.d.vel = 0; pk.held = false; jg.x.v = jg.y.v = jg.x.vel = jg.y.vel = 0;
}
const n = Math.max(1, Math.round(dt * 120));
@@ -515,18 +526,21 @@
let tickT = 0;
function tick() {
clearTimeout(tickT);
const t0 = performance.now();
tickT = setTimeout(() => {
if (running) return;
if (active() && idle() && mode === 'wait') { wait -= .25; if (wait <= 0) { appear(); if (running) return; } }
const el = Math.min((performance.now() - t0) / 1000, 1); /* 按真实经过的时间减(定时器会拖慢,按 .25 数会越数越长) */
if (!active()) { if (mode === 'wait') wait = gap(); } /* 11-05 不在首页 / 切后台:回来从头数 25~35 秒 */
else if (idle() && mode === 'wait') { wait -= el; if (wait <= 0) { appear(); if (running) return; } }
tick();
}, 250);
}
// 开关切换(「隐私权限」里那一行)
addEventListener('mb-bump', () => {
if (isOff()) { mode = 'wait'; ft.h.v = pk.d.v = 0; release(); cx.clearRect(0, 0, cv.width, cv.height); }
else { wait = 1.2; tick(); }
else { wait = gap(); tick(); }
});
// 调试 / 截图用:URL 带 bumpdebug 才挂
if (/bumpdebug/.test(location.href)) window.__bump = { ft, pk, atoms: () => atoms.map(a => [a.el.className || a.el.tagName, Math.round(a.x), Math.round(a.y), a.el.style.transform.slice(0, 90)]), mode: () => mode, now: () => { wait = 0; lastTouch = 0; }, week: () => week, at: (k, x, y) => { lastTouch = 0; appear(k, { x, y: y + scr.scrollTop }); } };
wait = 3; tick();
if (/bumpdebug/.test(location.href)) window.__bump = { ft, pk, atoms: () => atoms.map(a => [a.el.className || a.el.tagName, Math.round(a.x), Math.round(a.y), a.el.style.transform.slice(0, 90)]), mode: () => mode, w: () => [Math.round(wait * 10) / 10, Math.round(performance.now() - lastTouch), active(), running], now: () => { wait = 0; lastTouch = 0; }, week: () => week, at: (k, x, y) => { lastTouch = 0; appear(k, { x, y: y + scr.scrollTop }); } };
wait = gap(); tick();
})();