forked from EXT/VR180-Web-Player
Folder organisation
This commit is contained in:
200
src/vr180player/dom/two-d-control-panel.ts
Normal file
200
src/vr180player/dom/two-d-control-panel.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { setLucideIcon } from './icons.js';
|
||||
import { formatTime } from '../utils/time.js';
|
||||
|
||||
type TwoDControlPanelCallbacks = {
|
||||
onForward: () => void;
|
||||
onMute: () => void;
|
||||
onPlayPause: () => void;
|
||||
onRewind: () => void;
|
||||
onSeek: (progress: number) => void;
|
||||
};
|
||||
|
||||
type TwoDControlPanelOptions = {
|
||||
callbacks: TwoDControlPanelCallbacks;
|
||||
fullscreenTarget: HTMLElement;
|
||||
getIsActive: () => boolean;
|
||||
playerContainer: HTMLElement;
|
||||
title: string;
|
||||
};
|
||||
|
||||
const CONTROL_PANEL_HIDE_DELAY = 3000;
|
||||
|
||||
export class TwoDControlPanel {
|
||||
private readonly callbacks: TwoDControlPanelCallbacks;
|
||||
private readonly fullscreenTarget: HTMLElement;
|
||||
private readonly getIsActive: () => boolean;
|
||||
private readonly playerContainer: HTMLElement;
|
||||
private controlPanel: HTMLElement | null;
|
||||
private currentTimeDisplay: HTMLElement | null;
|
||||
private hideTimeout: number | undefined;
|
||||
private playedBar: HTMLElement | null;
|
||||
private progressBar: HTMLElement | null;
|
||||
private totalTimeDisplay: HTMLElement | null;
|
||||
private playButton: HTMLButtonElement | null;
|
||||
private muteButton: HTMLButtonElement | null;
|
||||
|
||||
constructor({ callbacks, fullscreenTarget, getIsActive, playerContainer, title }: TwoDControlPanelOptions) {
|
||||
this.callbacks = callbacks;
|
||||
this.fullscreenTarget = fullscreenTarget;
|
||||
this.getIsActive = getIsActive;
|
||||
this.playerContainer = playerContainer;
|
||||
|
||||
this.controlPanel = playerContainer.querySelector('.vrwp-panel');
|
||||
const videoTitle = playerContainer.querySelector<HTMLElement>('.vrwp-video-title');
|
||||
this.currentTimeDisplay = playerContainer.querySelector('.vrwp-current-time');
|
||||
this.totalTimeDisplay = playerContainer.querySelector('.vrwp-total-time');
|
||||
this.progressBar = playerContainer.querySelector('.vrwp-bar');
|
||||
this.playedBar = playerContainer.querySelector('.vrwp-played');
|
||||
this.playButton = playerContainer.querySelector('.vrwp-play-toggle');
|
||||
this.muteButton = playerContainer.querySelector('.vrwp-mute');
|
||||
|
||||
if (!this.controlPanel) {
|
||||
console.error('VR_WEB_PLAYER_DOM: 2D control panel was not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (videoTitle) {
|
||||
videoTitle.textContent = title;
|
||||
}
|
||||
|
||||
this.bindControls(playerContainer);
|
||||
}
|
||||
|
||||
show(): void {
|
||||
if (!this.getIsActive() || !this.controlPanel) return;
|
||||
|
||||
this.clearHideTimeout();
|
||||
this.controlPanel.classList.add('visible');
|
||||
this.hideTimeout = window.setTimeout(() => this.hide(), CONTROL_PANEL_HIDE_DELAY);
|
||||
}
|
||||
|
||||
showPersistent(): void {
|
||||
if (!this.getIsActive() || !this.controlPanel) return;
|
||||
|
||||
this.clearHideTimeout();
|
||||
this.controlPanel.classList.add('visible');
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
if (!this.controlPanel) return;
|
||||
|
||||
this.clearHideTimeout();
|
||||
this.controlPanel.classList.remove('visible');
|
||||
}
|
||||
|
||||
position(canvas: HTMLElement): void {
|
||||
if (!this.getIsActive() || !this.controlPanel) return;
|
||||
|
||||
const canvasRect = canvas.getBoundingClientRect();
|
||||
const containerRect = this.playerContainer.getBoundingClientRect();
|
||||
const bottomOffset = canvasRect.height * 0.1;
|
||||
const panelHeight = this.controlPanel.offsetHeight;
|
||||
const topPosition = (canvasRect.bottom - containerRect.top) - bottomOffset - panelHeight;
|
||||
|
||||
this.controlPanel.style.position = 'absolute';
|
||||
this.controlPanel.style.top = `${topPosition}px`;
|
||||
this.controlPanel.style.bottom = 'auto';
|
||||
this.controlPanel.style.left = '50%';
|
||||
this.controlPanel.style.transform = 'translateX(-50%)';
|
||||
this.controlPanel.style.zIndex = '1000';
|
||||
}
|
||||
|
||||
updateMuteButton(isMuted: boolean): void {
|
||||
if (!this.getIsActive() || !this.muteButton) return;
|
||||
|
||||
if (isMuted) {
|
||||
this.muteButton.classList.remove('muted');
|
||||
this.muteButton.classList.add('unmuted');
|
||||
setLucideIcon(this.muteButton, 'volume-x');
|
||||
} else {
|
||||
this.muteButton.classList.remove('unmuted');
|
||||
this.muteButton.classList.add('muted');
|
||||
setLucideIcon(this.muteButton, 'volume-2');
|
||||
}
|
||||
}
|
||||
|
||||
updatePlaybackButton(isPausedOrEnded: boolean): void {
|
||||
if (!this.getIsActive() || !this.playButton) return;
|
||||
|
||||
if (isPausedOrEnded) {
|
||||
this.playButton.classList.remove('playing');
|
||||
this.playButton.classList.add('paused');
|
||||
setLucideIcon(this.playButton, 'play');
|
||||
} else {
|
||||
this.playButton.classList.remove('paused');
|
||||
this.playButton.classList.add('playing');
|
||||
setLucideIcon(this.playButton, 'pause');
|
||||
}
|
||||
}
|
||||
|
||||
updateTime(currentTime: number, duration: number): void {
|
||||
if (!this.getIsActive()) return;
|
||||
|
||||
if (this.currentTimeDisplay) {
|
||||
this.currentTimeDisplay.textContent = formatTime(currentTime);
|
||||
}
|
||||
|
||||
if (this.totalTimeDisplay && isFinite(duration)) {
|
||||
this.totalTimeDisplay.textContent = formatTime(duration);
|
||||
}
|
||||
|
||||
if (this.playedBar && isFinite(duration) && duration > 0) {
|
||||
const progress = (currentTime / duration) * 100;
|
||||
this.playedBar.style.width = `${progress}%`;
|
||||
}
|
||||
}
|
||||
|
||||
private bindControls(playerContainer: HTMLElement): void {
|
||||
playerContainer.querySelector('.vrwp-fullscreen')?.addEventListener('click', () => {
|
||||
this.toggleFullscreen();
|
||||
});
|
||||
|
||||
playerContainer.querySelector('.vrwp-back')?.addEventListener('click', () => {
|
||||
this.callbacks.onRewind();
|
||||
this.show();
|
||||
});
|
||||
|
||||
this.playButton?.addEventListener('click', () => {
|
||||
this.callbacks.onPlayPause();
|
||||
this.show();
|
||||
});
|
||||
|
||||
playerContainer.querySelector('.vrwp-forward')?.addEventListener('click', () => {
|
||||
this.callbacks.onForward();
|
||||
this.show();
|
||||
});
|
||||
|
||||
this.muteButton?.addEventListener('click', () => {
|
||||
this.callbacks.onMute();
|
||||
this.show();
|
||||
});
|
||||
|
||||
this.progressBar?.addEventListener('click', (event) => {
|
||||
const rect = this.progressBar?.getBoundingClientRect();
|
||||
if (rect && rect.width > 0) {
|
||||
this.callbacks.onSeek((event.clientX - rect.left) / rect.width);
|
||||
}
|
||||
this.show();
|
||||
});
|
||||
}
|
||||
|
||||
private clearHideTimeout(): void {
|
||||
if (this.hideTimeout !== undefined) {
|
||||
clearTimeout(this.hideTimeout);
|
||||
this.hideTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private toggleFullscreen(): void {
|
||||
if (!document.fullscreenElement) {
|
||||
this.fullscreenTarget.requestFullscreen().catch((err) => {
|
||||
console.error('Error attempting to enable fullscreen:', err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
document.exitFullscreen().catch((err) => {
|
||||
console.error('Error attempting to exit fullscreen:', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user