not sure
This commit is contained in:
parent
560398fefb
commit
80ed37298b
73 changed files with 3619 additions and 1295 deletions
89
src/index.ts
89
src/index.ts
|
|
@ -11,6 +11,7 @@ import { FullScreenHandler } from './page/full-screen-handler';
|
|||
import { MenuHider } from './page/menu-hider';
|
||||
import { activeVibe, applyVibeSettings, resetSettings, settings } from './settings';
|
||||
import { readBrowserStorage, writeBrowserStorage } from './utils/browser-storage';
|
||||
import { clamp } from './utils/clamp';
|
||||
import { DeltaTimeCalculator } from './utils/delta-time-calculator';
|
||||
import { queryRequiredElement, queryRequiredElements } from './utils/dom';
|
||||
import {
|
||||
|
|
@ -51,8 +52,30 @@ const mirrorSegmentNames: Readonly<Record<number, string>> =
|
|||
|
||||
const formatMirrorSegmentCount = (count: number): string =>
|
||||
count === appConfig.toolbar.mirror.default
|
||||
? 'Mirror off'
|
||||
: `${count} ${mirrorSegmentNames[count] ?? 'slices'}`;
|
||||
? appConfig.toolbar.mirror.offLabel
|
||||
: `${count} ${mirrorSegmentNames[count] ?? appConfig.toolbar.mirror.fallbackSegmentName}`;
|
||||
|
||||
const clampAudioVolume = (value: number): number => {
|
||||
const safeValue = Number.isFinite(value) ? value : appConfig.toolbar.volume.default;
|
||||
return clamp(safeValue, appConfig.toolbar.volume.min, appConfig.toolbar.volume.max);
|
||||
};
|
||||
|
||||
const getAudioVolumeRatio = (volume: number): number =>
|
||||
(volume - appConfig.toolbar.volume.min) /
|
||||
(appConfig.toolbar.volume.max - appConfig.toolbar.volume.min);
|
||||
|
||||
const getAudioVolumePercent = (volume: number): number =>
|
||||
Math.round(getAudioVolumeRatio(volume) * 100);
|
||||
|
||||
const readInitialAudioVolume = (): number => {
|
||||
const storedVolume = readBrowserStorage(appConfig.storage.audioVolumeKey);
|
||||
return storedVolume === null
|
||||
? appConfig.toolbar.volume.default
|
||||
: clampAudioVolume(Number(storedVolume));
|
||||
};
|
||||
|
||||
const formatStoredAudioVolume = (volume: number): string =>
|
||||
clampAudioVolume(volume).toFixed(2);
|
||||
|
||||
type RuntimeUiError = Parameters<
|
||||
Parameters<typeof ErrorHandler.addOnErrorListener>[0]
|
||||
|
|
@ -112,6 +135,8 @@ const queryAppElements = () => ({
|
|||
),
|
||||
settingsButton: queryRequiredElement('button.settings', HTMLButtonElement),
|
||||
soundButton: queryRequiredElement('button.sound', HTMLButtonElement),
|
||||
volumeControl: queryRequiredElement('.volume-control', HTMLLabelElement),
|
||||
volumeSlider: queryRequiredElement('.volume-slider', HTMLInputElement),
|
||||
restartButton: queryRequiredElement('button.restart', HTMLButtonElement),
|
||||
canvas: queryRequiredElement('canvas', HTMLCanvasElement),
|
||||
eraserPreview: queryRequiredElement('.eraser-preview', HTMLDivElement),
|
||||
|
|
@ -141,18 +166,49 @@ const setLoadingStage = (label: string, ratio: number) => {
|
|||
elements.loadingProgress.setAttribute('aria-valuenow', String(percent));
|
||||
};
|
||||
|
||||
let isAudioMuted = readBrowserStorage(appConfig.storage.audioMutedKey) === '1';
|
||||
let audioVolume = readInitialAudioVolume();
|
||||
let isAudioMuted =
|
||||
readBrowserStorage(appConfig.storage.audioMutedKey) === '1' ||
|
||||
audioVolume <= appConfig.toolbar.volume.min;
|
||||
let isEraserActive = false;
|
||||
|
||||
const persistAudioUiState = () => {
|
||||
writeBrowserStorage(appConfig.storage.audioMutedKey, isAudioMuted ? '1' : '0');
|
||||
writeBrowserStorage(
|
||||
appConfig.storage.audioVolumeKey,
|
||||
formatStoredAudioVolume(audioVolume)
|
||||
);
|
||||
};
|
||||
|
||||
const renderAudioUi = (game: GameLoop | null) => {
|
||||
elements.soundButton.classList.toggle('muted', isAudioMuted);
|
||||
elements.soundButton.setAttribute('aria-pressed', String(isAudioMuted));
|
||||
audioVolume = clampAudioVolume(audioVolume);
|
||||
const isEffectivelyMuted = isAudioMuted || audioVolume <= appConfig.toolbar.volume.min;
|
||||
const volumePercent = getAudioVolumePercent(audioVolume);
|
||||
|
||||
elements.soundButton.classList.toggle('muted', isEffectivelyMuted);
|
||||
elements.soundButton.setAttribute('aria-pressed', String(isEffectivelyMuted));
|
||||
elements.soundButton.setAttribute(
|
||||
'aria-label',
|
||||
isAudioMuted ? 'Unmute audio' : 'Mute audio'
|
||||
isEffectivelyMuted ? 'Unmute audio' : 'Mute audio'
|
||||
);
|
||||
elements.soundButton.title = isAudioMuted ? 'Unmute audio' : 'Mute audio';
|
||||
game?.setAudioMuted(isAudioMuted);
|
||||
elements.soundButton.title = isEffectivelyMuted ? 'Unmute audio' : 'Mute audio';
|
||||
|
||||
elements.volumeSlider.min = appConfig.toolbar.volume.min.toString();
|
||||
elements.volumeSlider.max = appConfig.toolbar.volume.max.toString();
|
||||
elements.volumeSlider.step = appConfig.toolbar.volume.step.toString();
|
||||
elements.volumeSlider.value = formatStoredAudioVolume(audioVolume);
|
||||
elements.volumeSlider.setAttribute(
|
||||
'aria-valuetext',
|
||||
isEffectivelyMuted ? `Muted, ${volumePercent}%` : `${volumePercent}%`
|
||||
);
|
||||
elements.volumeControl.classList.toggle('muted', isEffectivelyMuted);
|
||||
elements.volumeControl.title = isEffectivelyMuted
|
||||
? `Muted, ${volumePercent}% volume`
|
||||
: `${volumePercent}% volume`;
|
||||
elements.volumeControl.style.setProperty('--volume-progress', `${volumePercent}%`);
|
||||
|
||||
game?.setAudioVolume(audioVolume);
|
||||
game?.setAudioMuted(isEffectivelyMuted);
|
||||
};
|
||||
|
||||
const renderPaletteUi = (game: GameLoop | null) => {
|
||||
|
|
@ -322,8 +378,21 @@ const main = async () => {
|
|||
|
||||
elements.restartButton.addEventListener('click', () => game?.destroy());
|
||||
elements.soundButton.addEventListener('click', () => {
|
||||
isAudioMuted = !isAudioMuted;
|
||||
writeBrowserStorage(appConfig.storage.audioMutedKey, isAudioMuted ? '1' : '0');
|
||||
const shouldUnmute = isAudioMuted || audioVolume <= appConfig.toolbar.volume.min;
|
||||
if (shouldUnmute && audioVolume <= appConfig.toolbar.volume.min) {
|
||||
audioVolume = appConfig.toolbar.volume.default;
|
||||
}
|
||||
isAudioMuted = !shouldUnmute;
|
||||
persistAudioUiState();
|
||||
renderAudioUi(game);
|
||||
if (!isAudioMuted) {
|
||||
game?.startAudio(true);
|
||||
}
|
||||
});
|
||||
elements.volumeSlider.addEventListener('input', () => {
|
||||
audioVolume = clampAudioVolume(Number(elements.volumeSlider.value));
|
||||
isAudioMuted = audioVolume <= appConfig.toolbar.volume.min;
|
||||
persistAudioUiState();
|
||||
renderAudioUi(game);
|
||||
if (!isAudioMuted) {
|
||||
game?.startAudio(true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue