UNPKG

playable

Version:

Video player based on HTML5Video

296 lines (239 loc) 7.15 kB
import View from '../core/view'; import { IView } from '../core/types'; import { screenTemplate } from './templates'; import htmlToElement from '../core/htmlToElement'; import getElementByHook from '../core/getElementByHook'; import toggleElementClass from '../core/toggleElementClass'; import { VideoViewMode, IScreenViewStyles, IScreenViewCallbacks, IScreenViewConfig, } from './types'; import styles from './screen.scss'; class ScreenView extends View<IScreenViewStyles> implements IView<IScreenViewStyles> { private _callbacks: IScreenViewCallbacks; private _$rootElement: HTMLElement; private _$canvas: HTMLCanvasElement; private _$playbackElement: HTMLVideoElement; private _isHorizontalStripes: boolean; private _requestAnimationFrameID: number; private _currentMode: string; private _styleNamesByViewMode: { [viewMode: string]: string; }; constructor(config: IScreenViewConfig) { super(); const { callbacks, nativeControls, playbackViewElement } = config; this._callbacks = callbacks; this._styleNamesByViewMode = { [VideoViewMode.REGULAR]: this.styleNames.regularMode, [VideoViewMode.BLUR]: this.styleNames.blurMode, [VideoViewMode.FILL]: this.styleNames.fillMode, }; this._bindCallbacks(); if (nativeControls) { playbackViewElement.setAttribute('controls', 'true'); } this._initDOM(playbackViewElement); this._bindEvents(); this.setViewMode(VideoViewMode.REGULAR); } private _bindCallbacks() { this._updateBackground = this._updateBackground.bind(this); } private _initDOM(playbackViewElement: HTMLElement) { this._$rootElement = htmlToElement( screenTemplate({ styles: this.styleNames, }), ); this._$playbackElement = playbackViewElement as HTMLVideoElement; this._$rootElement.appendChild(playbackViewElement); this._$canvas = getElementByHook( this._$rootElement, 'background-canvas', ) as HTMLCanvasElement; } private _bindEvents() { this._$rootElement.addEventListener( 'click', this._callbacks.onWrapperMouseClick, ); this._$rootElement.addEventListener( 'dblclick', this._callbacks.onWrapperMouseDblClick, ); } private _unbindEvents() { this._$rootElement.removeEventListener( 'click', this._callbacks.onWrapperMouseClick, ); this._$rootElement.removeEventListener( 'dblclick', this._callbacks.onWrapperMouseDblClick, ); } focusOnNode() { this._$rootElement.focus(); } show() { toggleElementClass(this._$rootElement, this.styleNames.hidden, false); } hide() { toggleElementClass(this._$rootElement, this.styleNames.hidden, true); } getElement() { return this._$rootElement; } hideCursor() { toggleElementClass(this._$rootElement, this.styleNames.hiddenCursor, true); } showCursor() { toggleElementClass(this._$rootElement, this.styleNames.hiddenCursor, false); } setViewMode(viewMode: VideoViewMode) { if (this._styleNamesByViewMode[viewMode]) { this.resetBackground(); Object.keys(this._styleNamesByViewMode).forEach(mode => { toggleElementClass( this._$rootElement, this._styleNamesByViewMode[mode], false, ); }); toggleElementClass( this._$rootElement, this._styleNamesByViewMode[viewMode], true, ); if (viewMode === VideoViewMode.BLUR) { this._startUpdatingBackground(); } else { this._stopUpdatingBackground(); } this._currentMode = viewMode; } } setBackgroundSize(width: number, height: number) { this.setBackgroundWidth(width); this.setBackgroundHeight(height); } setBackgroundWidth(width: number) { this._$canvas.width = width; } setBackgroundHeight(height: number) { this._$canvas.height = height; } private _startUpdatingBackground() { if (!this._requestAnimationFrameID) { this._updateBackground(); } } private _stopUpdatingBackground() { if (this._requestAnimationFrameID) { cancelAnimationFrame(this._requestAnimationFrameID); this._requestAnimationFrameID = null; } } resetAspectRatio() { const { videoWidth, videoHeight } = this._$playbackElement; const { width, height } = this._$rootElement.getBoundingClientRect(); this._isHorizontalStripes = width / height < (videoHeight ? videoWidth / videoHeight : 0); toggleElementClass( this._$rootElement, this.styleNames.horizontalStripes, this._isHorizontalStripes, ); toggleElementClass( this._$rootElement, this.styleNames.verticalStripes, !this._isHorizontalStripes, ); } resetBackground() { if (this._currentMode === VideoViewMode.BLUR) { this._clearBackground(); } } private _getSourceAreas(width: number, height: number): number[][] { if (this._isHorizontalStripes) { return [ [0, 0, width, 1], [0, height - 1, width, 1], ]; } return [ [0, 0, 1, height], [width - 1, 0, 1, height], ]; } private _getCanvasAreas(width: number, height: number): number[][] { if (this._isHorizontalStripes) { return [ [0, 0, width, height / 2], [0, height / 2, width, height / 2], ]; } return [ [0, 0, width / 2, height], [width / 2, 0, width / 2, height], ]; } private _drawAreaFromSource(source: number[], area: number[]) { const [sourceX, sourceY, sourceWidth, sourceHeight] = source; const [areaX, areaY, areaWidth, areaHeight] = area; const ctx = this._$canvas.getContext('2d'); ctx.drawImage( this._$playbackElement, sourceX, sourceY, sourceWidth, sourceHeight, areaX, areaY, areaWidth, areaHeight, ); } private _drawBackground() { const { videoWidth, videoHeight } = this._$playbackElement; const canvasWidth: number = this._$canvas.width; const canvasHeight: number = this._$canvas.height; const sourceAreas: number[][] = this._getSourceAreas( videoWidth, videoHeight, ); const canvasAreas: number[][] = this._getCanvasAreas( canvasWidth, canvasHeight, ); this._drawAreaFromSource(sourceAreas[0], canvasAreas[0]); this._drawAreaFromSource(sourceAreas[1], canvasAreas[1]); } private _updateBackground() { this._drawBackground(); this._requestAnimationFrameID = requestAnimationFrame( this._updateBackground, ); } private _clearBackground() { const ctx = this._$canvas.getContext('2d'); ctx.clearRect(0, 0, this._$canvas.width, this._$canvas.height); } destroy() { this._stopUpdatingBackground(); this._unbindEvents(); if (this._$rootElement.parentNode) { this._$rootElement.parentNode.removeChild(this._$rootElement); } this._$rootElement = null; this._$playbackElement = null; this._$canvas = null; } } ScreenView.extendStyleNames(styles); export default ScreenView;