import { hideVrPanelImmediately, setVrPanelOpacity, type VrControlPanel } from './vr-control-panel.js'; import { DEFAULT_MENU_AUTO_HIDE_DELAY_MS } from '../utils/control-panel-timing.js'; const FADE_DURATION_MS = 200; export class VrPanelVisibility { private hideTimeout: number | undefined; private lastFadeTimestamp = 0; private opacity = 0; private panel: VrControlPanel | undefined; private targetOpacity = 0; private fading = false; get isFading(): boolean { return this.fading; } get isVisible(): boolean { return !!(this.panel?.group.visible && this.opacity > 0.01); } setPanel(panel: VrControlPanel): void { this.panel = panel; this.hideImmediately(); } show(autoHideDelayMs = DEFAULT_MENU_AUTO_HIDE_DELAY_MS): void { this.showWithAutoHide(true, autoHideDelayMs); } showPersistent(): void { this.showWithAutoHide(false, DEFAULT_MENU_AUTO_HIDE_DELAY_MS); } private showWithAutoHide(shouldAutoHide: boolean, autoHideDelayMs: number): void { if (this.panel) this.panel.group.visible = true; this.clearHideTimeout(); if (this.targetOpacity !== 1.0 || this.opacity < 1.0) { this.targetOpacity = 1.0; this.startFade(); } if (shouldAutoHide) { this.hideTimeout = window.setTimeout(() => this.hide(), autoHideDelayMs); } } hide(): void { this.clearHideTimeout(); if (this.targetOpacity !== 0.0 || this.opacity > 0.0) { this.targetOpacity = 0.0; this.startFade(); } } hideImmediately(): void { this.clearHideTimeout(); this.targetOpacity = 0; this.opacity = 0; this.fading = false; hideVrPanelImmediately(this.panel); } updateFade(timestamp: number): void { if (!this.panel) return; if (this.lastFadeTimestamp === 0) this.lastFadeTimestamp = timestamp; const deltaTime = (timestamp - this.lastFadeTimestamp) / 1000; this.lastFadeTimestamp = timestamp; const fadeSpeed = 1 / (FADE_DURATION_MS / 1000); let opacityChanged = false; if (this.opacity < this.targetOpacity) { this.opacity += fadeSpeed * deltaTime; if (this.opacity >= this.targetOpacity) { this.opacity = this.targetOpacity; this.fading = false; } opacityChanged = true; } else if (this.opacity > this.targetOpacity) { this.opacity -= fadeSpeed * deltaTime; if (this.opacity <= this.targetOpacity) { this.opacity = this.targetOpacity; this.fading = false; if (this.opacity === 0) this.panel.group.visible = false; } opacityChanged = true; } else { this.fading = false; } if (opacityChanged) { setVrPanelOpacity(this.panel, this.opacity); } if (this.fading) { requestAnimationFrame((nextTimestamp) => this.updateFade(nextTimestamp)); } } private clearHideTimeout(): void { if (this.hideTimeout !== undefined) { clearTimeout(this.hideTimeout); this.hideTimeout = undefined; } } private startFade(): void { if (!this.fading) { this.fading = true; this.lastFadeTimestamp = 0; requestAnimationFrame((timestamp) => this.updateFade(timestamp)); } } }