move.gl
Version:
Motion and Animation Library for Stylescape.
206 lines (178 loc) • 6 kB
text/typescript
// ============================================================================
// move.gl | Video Overlay
// ============================================================================
// Copyright 2025 Scape Agency BV
// Licensed under MIT License
// ============================================================================
/**
* Video overlay options
*/
export interface VideoOverlayOptions {
/** Fade transition duration in milliseconds */
fadeTransitionDuration?: number;
/** Whether to loop the video */
loop?: boolean;
/** Initial video source URL */
initialSource?: string;
}
/**
* Transparent Video Overlay Handler
*
* Manages a transparent video overlay, controlling its visibility,
* playback, and effects.
*
* @example
* ```typescript
* const overlay = new TransparentVideoOverlay('myVideo', {
* fadeTransitionDuration: 500,
* loop: true
* });
* overlay.showOverlay();
* ```
*/
export class TransparentVideoOverlay {
private videoElement: HTMLVideoElement | null = null;
private isVisible: boolean = false;
private fadeTransitionDuration: number;
private loop: boolean;
/**
* Creates a new TransparentVideoOverlay instance.
* @param videoElementId - The ID of the video element to manage.
* @param options - Optional configuration options.
*/
constructor(videoElementId: string, options: VideoOverlayOptions = {}) {
const element = document.getElementById(videoElementId);
if (element instanceof HTMLVideoElement) {
this.videoElement = element;
} else {
console.warn(`Element with id "${videoElementId}" is not a video element`);
}
this.fadeTransitionDuration = options.fadeTransitionDuration ?? 500;
this.loop = options.loop ?? true;
if (this.videoElement) {
this.setupVideo();
if (options.initialSource) {
this.changeVideoSource(options.initialSource, false);
}
}
}
/**
* Initializes video settings and event listeners.
*/
private setupVideo(): void {
if (!this.videoElement) return;
if (this.loop) {
this.videoElement.addEventListener('ended', () => {
this.videoElement?.play();
});
}
this.videoElement.addEventListener('loadeddata', () => {
console.log('Video loaded successfully.');
});
this.videoElement.addEventListener('error', (e) => {
console.error('Error loading video:', e);
});
// Set initial style for smooth transitions
this.videoElement.style.transition = `opacity ${this.fadeTransitionDuration}ms ease`;
}
/**
* Shows the video overlay with a fade-in effect.
*/
public showOverlay(): void {
if (!this.videoElement) return;
this.videoElement.style.display = 'block';
this.videoElement.style.opacity = '0';
// Use requestAnimationFrame for smoother transition
requestAnimationFrame(() => {
if (this.videoElement) {
this.videoElement.style.opacity = '1';
this.videoElement.play().catch(err => {
console.warn('Auto-play prevented:', err);
});
}
});
this.isVisible = true;
}
/**
* Hides the video overlay with a fade-out effect.
*/
public hideOverlay(): void {
if (!this.videoElement) return;
this.videoElement.style.opacity = '0';
setTimeout(() => {
if (this.videoElement) {
this.videoElement.style.display = 'none';
this.videoElement.pause();
}
}, this.fadeTransitionDuration);
this.isVisible = false;
}
/**
* Toggles the visibility of the video overlay.
*/
public toggleOverlay(): void {
if (this.isVisible) {
this.hideOverlay();
} else {
this.showOverlay();
}
}
/**
* Changes the video source and optionally plays it immediately.
* @param videoUrl - The URL of the new video source.
* @param autoPlay - Whether the video should play immediately after loading.
*/
public changeVideoSource(videoUrl: string, autoPlay: boolean = true): void {
if (!this.videoElement) return;
this.videoElement.src = videoUrl;
this.videoElement.load();
if (autoPlay) {
this.showOverlay();
}
}
/**
* Gets the visibility state of the overlay.
*/
public getIsVisible(): boolean {
return this.isVisible;
}
/**
* Cleans up the video overlay instance.
*/
public destroy(): void {
if (this.videoElement) {
this.videoElement.pause();
this.videoElement.src = '';
this.videoElement = null;
}
}
}
/**
* Checks if the browser supports HEVC alpha channel videos.
* This is primarily supported in Safari.
* @returns Whether HEVC alpha is supported.
*/
export function supportsHEVCAlpha(): boolean {
const navigator = window.navigator;
const ua = navigator.userAgent.toLowerCase();
const hasMediaCapabilities = !!(
navigator.mediaCapabilities &&
navigator.mediaCapabilities.decodingInfo
);
const isSafari = (
ua.indexOf('safari') !== -1 &&
ua.indexOf('chrome') === -1 &&
ua.indexOf('version/') !== -1
);
return isSafari && hasMediaCapabilities;
}
/**
* Gets the appropriate video source based on browser support.
* @param hevcSource - The HEVC/MOV source for Safari.
* @param webmSource - The WebM source for other browsers.
* @returns The appropriate video source URL.
*/
export function getOptimalVideoSource(hevcSource: string, webmSource: string): string {
return supportsHEVCAlpha() ? hevcSource : webmSource;
}
export default TransparentVideoOverlay;