第二批反馈:引擎改造(限幅/SoundTouch不变调变速/长按快进/跳句/iOS闪光降级) + 6游戏状态BGM与试玩测试全绿

This commit is contained in:
SoundGame Dev
2026-08-30 16:45:42 +08:00
parent cc0f8c008d
commit 9a915d62c0
1163 changed files with 6346 additions and 208 deletions
+55 -10
View File
@@ -15,8 +15,12 @@
* | beat | [8] | 节拍锚点(最轻) |
* | danger | [25,40,25,40,25] | 危险逼近 |
*
* 特性检测:'vibrate' in navigator。不支持的平台(iOS Safari 等)
* 所有调用静默 no-op,不抛异常。
* 特性检测:'vibrate' in navigator。支持的平台正常震动;
* **不支持的平台(iOS Safari 等)降级为视觉反馈**——全屏边框闪光覆盖层
* (#haptics-flash,样式在 style.css),不同震型对应不同颜色与闪烁次数:
* confirm 白·1 次 / error 红·2 次 / success 绿·1 次 / warning 橙·3 次 /
* swipe 白·极淡 1 次 / heartbeat 粉·2 次 / pickup 青·1 次 / hit 红·强 1 次 /
* beat 白·极淡 1 次 / danger 红·快 3 次。两者都不行的环境静默 no-op。
*
* 用法:
* import { vibrate } from '../engine/haptics.js';
@@ -41,26 +45,67 @@ export const HAPTIC_PATTERNS = {
danger: [25, 40, 25, 40, 25],
};
/** 当前环境是否支持震动(iOS Safari 为 false)。 */
/** 当前环境是否支持震动(iOS Safari 为 false,此时 vibrate() 走视觉降级)。 */
export const hapticsSupported =
typeof navigator !== 'undefined' && 'vibrate' in navigator;
/** 震型 → 视觉降级闪光参数:[颜色, 动画 class, 亮度] */
const FLASH_MAP = {
confirm: ['#ffffff', 'hp-once', 0.7],
error: ['#ff5252', 'hp-twice', 0.9],
success: ['#69f0ae', 'hp-once', 0.9],
warning: ['#ffb74d', 'hp-thrice', 0.9],
swipe: ['#ffffff', 'hp-once', 0.25],
heartbeat: ['#f48fb1', 'hp-twice', 0.7],
pickup: ['#80deea', 'hp-once', 0.7],
hit: ['#ff1744', 'hp-once', 1],
beat: ['#ffffff', 'hp-once', 0.15],
danger: ['#ff5252', 'hp-thrice', 0.9],
};
let flashEl = null;
/** 视觉降级:全屏边框闪光(覆盖层常驻 body,改 class 重触发动画)。 */
function _flash(patternName) {
if (typeof document === 'undefined') return;
const [color, anim, alpha] = FLASH_MAP[patternName] || FLASH_MAP.confirm;
if (!flashEl) {
flashEl = document.getElementById('haptics-flash');
if (!flashEl) {
flashEl = document.createElement('div');
flashEl.id = 'haptics-flash';
document.body.appendChild(flashEl);
}
}
flashEl.style.borderColor = color;
flashEl.style.boxShadow = `inset 0 0 32px ${color}`;
flashEl.style.setProperty('--hp-alpha', alpha);
flashEl.style.opacity = '';
flashEl.className = '';
void flashEl.offsetWidth; // 强制 reflow,重触发动画
flashEl.className = anim;
}
/**
* 触发震动。不支持时静默 no-op。
* 触发震动;不支持 vibrate 的平台自动降级为边框闪光。
* @param {keyof typeof HAPTIC_PATTERNS | number[]} pattern 预定义名称或毫秒数组
*/
export function vibrate(pattern) {
if (!hapticsSupported) return;
const p = typeof pattern === 'string' ? HAPTIC_PATTERNS[pattern] : pattern;
const name = typeof pattern === 'string' ? pattern : null;
const p = name ? HAPTIC_PATTERNS[name] : pattern;
if (!p) {
console.warn('[haptics] 未知震型:', pattern);
return;
}
try {
navigator.vibrate(p);
} catch (_) {
// 某些平台调用即抛(极少见)→ 静默
if (hapticsSupported) {
try {
navigator.vibrate(p);
return;
} catch (_) {
// 某些平台调用即抛(极少见)→ 落入视觉降级
}
}
_flash(name || 'confirm'); // 自定义数组无语义名,按 confirm 闪
}
export default vibrate;