From 6af3d5e80c76e83a828d93a4666cbef039e63dc2 Mon Sep 17 00:00:00 2001 From: Karlcarlo Date: Fri, 2 Oct 2026 08:03:12 +0000 Subject: [PATCH] =?UTF-8?q?=E6=82=84=E6=82=84=E8=AF=9D=E5=8D=81=E4=B8=89?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=E6=AF=8F=E9=A2=97=E6=98=9F=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E9=9F=B3=E7=AC=A6=EF=BC=8C=E4=BB=8E=E5=B7=A6?= =?UTF-8?q?=E5=88=B0=E5=8F=B3=E4=BA=94=E5=A3=B0=E9=9F=B3=E9=98=B6=E7=94=B1?= =?UTF-8?q?=E4=BD=8E=E5=88=B0=E9=AB=98=20(v26100177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- index.html | 2 +- js/app.js | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 46fb4a2..7fa18d1 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ 兜爸心选 · 孕育陪伴 - + diff --git a/js/app.js b/js/app.js index 9a874f9..b765ab7 100644 --- a/js/app.js +++ b/js/app.js @@ -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 ''; + '" 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) + '">'; }).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();