悄悄话十三改:每颗星一个固定音符,从左到右五声音阶由低到高 (v26100177)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<title>兜爸心选 · 孕育陪伴</title>
|
<title>兜爸心选 · 孕育陪伴</title>
|
||||||
<!-- V:26100176 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
<!-- V:26100177 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||||
<link rel="stylesheet" href="mama-box.css">
|
<link rel="stylesheet" href="mama-box.css">
|
||||||
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||||
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
|
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
|
||||||
|
|||||||
27
js/app.js
27
js/app.js
@@ -3,7 +3,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||||
var VER = '26100176';
|
var VER = '26100177';
|
||||||
const $ = (s, c) => (c || document).querySelector(s);
|
const $ = (s, c) => (c || document).querySelector(s);
|
||||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(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 RM = !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches);
|
||||||
const AURA = [['#6B4AA8', '#C2667A'], ['#3F4FA6', '#8E5BB0'], ['#9A4F86', '#E0907A'], ['#365C9E', '#7A58AE'], ['#7D3F8C', '#D27A9A']];
|
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 hsh = s => { let h = 7; for (const c of String(s)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return h; };
|
||||||
const card = $('#wsc');
|
const card = $('#wsc');
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
@@ -3371,13 +3373,19 @@
|
|||||||
function paintSky(l) {
|
function paintSky(l) {
|
||||||
const n = l.length, cols = n > 16 ? 5 : 4, rows = Math.max(2, Math.ceil(n / cols));
|
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 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) => {
|
$('#wskBright').innerHTML = l.map((it, i) => {
|
||||||
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols);
|
const h = hsh(it.id), r = Math.floor(i / cols);
|
||||||
const x = 36 + ((c + .5 + ((h % 100) / 100 - .5) * .7) / cols) * 58;
|
const x = xs[i];
|
||||||
const y = 21 + ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .6) / rows) * 38;
|
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);
|
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' : '') +
|
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('');
|
}).join('');
|
||||||
}
|
}
|
||||||
function paintList(l) {
|
function paintList(l) {
|
||||||
@@ -3442,11 +3450,11 @@
|
|||||||
} catch (e) { return null; }
|
} catch (e) { return null; }
|
||||||
return actx;
|
return actx;
|
||||||
}
|
}
|
||||||
const NOTES = [1046.5, 1174.7, 1318.5, 1568, 1760, 2093];
|
function chime(f) {
|
||||||
function chime() {
|
|
||||||
const c = ac();
|
const c = ac();
|
||||||
if (!c) return;
|
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();
|
const g = c.createGain();
|
||||||
g.gain.setValueAtTime(0, t);
|
g.gain.setValueAtTime(0, t);
|
||||||
g.gain.linearRampToValueAtTime(.16, t + .012);
|
g.gain.linearRampToValueAtTime(.16, t + .012);
|
||||||
@@ -3502,10 +3510,11 @@
|
|||||||
seen.clear();
|
seen.clear();
|
||||||
$('#wskDone').classList.remove('show');
|
$('#wskDone').classList.remove('show');
|
||||||
paintSky(list());
|
paintSky(list());
|
||||||
|
/* 星星回来:从低到高刷一遍琴弦 */
|
||||||
|
PENTA.slice(0, 8).forEach((f, k) => setTimeout(() => chime(f), k * 70));
|
||||||
$('#wskBright').classList.remove('back');
|
$('#wskBright').classList.remove('back');
|
||||||
void $('#wskBright').offsetWidth;
|
void $('#wskBright').offsetWidth;
|
||||||
$('#wskBright').classList.add('back');
|
$('#wskBright').classList.add('back');
|
||||||
chime();
|
|
||||||
});
|
});
|
||||||
const stopAll = () => [...mts.values()].forEach(m => stopMeteor(m));
|
const stopAll = () => [...mts.values()].forEach(m => stopMeteor(m));
|
||||||
/* 第一次进来:一闪一闪的提示,点过一次星星就永久收起 */
|
/* 第一次进来:一闪一闪的提示,点过一次星星就永久收起 */
|
||||||
@@ -3555,7 +3564,7 @@
|
|||||||
btn.classList.add('flare');
|
btn.classList.add('flare');
|
||||||
btn.classList.remove('used');
|
btn.classList.remove('used');
|
||||||
$('#wskDone').classList.remove('show');
|
$('#wskDone').classList.remove('show');
|
||||||
chime();
|
chime(+btn.dataset.note);
|
||||||
tapFx(x0, y0, it.mine);
|
tapFx(x0, y0, it.mine);
|
||||||
let swish = false, dustT = 0;
|
let swish = false, dustT = 0;
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
|
|||||||
Reference in New Issue
Block a user