悄悄话十三改:每颗星一个固定音符,从左到右五声音阶由低到高 (v26100177)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 08:03:12 +00:00
parent bb87127fa8
commit 6af3d5e80c
2 changed files with 19 additions and 10 deletions

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26100176';
var VER = '26100177';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3349,6 +3349,8 @@
· 底部内容卡同时出现:整句 + 孕周 + 抱抱 + 「下一颗」。流星在卡片下面穿过,不挡字。 */
const RM = !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches);
const AURA = [['#6B4AA8', '#C2667A'], ['#3F4FA6', '#8E5BB0'], ['#9A4F86', '#E0907A'], ['#365C9E', '#7A58AE'], ['#7D3F8C', '#D27A9A']];
/* 五声音阶 C4 D4 E4 G4 A4 C5 …(18 个音,C4 到 E7,一屏亮星一颗一个) */
const PENTA = Array.from({ length: 18 }, (_, k) => 440 * Math.pow(2, (60 + [0, 2, 4, 7, 9][k % 5] + 12 * Math.floor(k / 5) - 69) / 12));
const hsh = s => { let h = 7; for (const c of String(s)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return h; };
const card = $('#wsc');
const seen = new Set();
@@ -3371,13 +3373,19 @@
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));
const xs = l.map((it, i) => 36 + ((i % cols + .5 + ((hsh(it.id) % 100) / 100 - .5) * .7) / cols) * 58);
/* 十三改(用户:「每点一下都是一个音符,每一个星星代表不同的音符」):
按横向位置从左到右排成一架竖琴——越靠左越低、越靠右越高,五声音阶(C4 起),连着点就是一段旋律 */
const rank = xs.map((x, i) => i).sort((p, q) => xs[p] - xs[q]);
const note = [];
rank.forEach((i, k) => { note[i] = PENTA[k % PENTA.length]; });
$('#wskBright').innerHTML = l.map((it, i) => {
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols);
const x = 36 + ((c + .5 + ((h % 100) / 100 - .5) * .7) / cols) * 58;
const h = hsh(it.id), r = Math.floor(i / cols);
const x = xs[i];
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 '<button type="button" class="wsk-st' + (it.mine ? ' mine' : '') + (seen.has(it.id) ? ' used' : '') + (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>';
'" data-go="' + (i + 1) + '" data-note="' + note[i].toFixed(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) {
@@ -3442,11 +3450,11 @@
} catch (e) { return null; }
return actx;
}
const NOTES = [1046.5, 1174.7, 1318.5, 1568, 1760, 2093];
function chime() {
function chime(f) {
const c = ac();
if (!c) return;
const t = c.currentTime, f = NOTES[Math.floor(Math.random() * NOTES.length)];
const t = c.currentTime;
f = f || PENTA[5 + Math.floor(Math.random() * 6)];
const g = c.createGain();
g.gain.setValueAtTime(0, t);
g.gain.linearRampToValueAtTime(.16, t + .012);
@@ -3502,10 +3510,11 @@
seen.clear();
$('#wskDone').classList.remove('show');
paintSky(list());
/* 星星回来:从低到高刷一遍琴弦 */
PENTA.slice(0, 8).forEach((f, k) => setTimeout(() => chime(f), k * 70));
$('#wskBright').classList.remove('back');
void $('#wskBright').offsetWidth;
$('#wskBright').classList.add('back');
chime();
});
const stopAll = () => [...mts.values()].forEach(m => stopMeteor(m));
/* 第一次进来:一闪一闪的提示,点过一次星星就永久收起 */
@@ -3555,7 +3564,7 @@
btn.classList.add('flare');
btn.classList.remove('used');
$('#wskDone').classList.remove('show');
chime();
chime(+btn.dataset.note);
tapFx(x0, y0, it.mine);
let swish = false, dustT = 0;
const t0 = performance.now();