bitmovin-player-ui
Version:
Bitmovin Player UI Framework
109 lines (90 loc) • 3.33 kB
text/typescript
import { Component, ComponentConfig } from './Component';
import { DOM } from '../DOM';
/**
* Animated analog TV static noise.
*
* @category Components
*/
export class TvNoiseCanvas extends Component<ComponentConfig> {
private canvas: DOM;
private canvasElement: HTMLCanvasElement;
private canvasContext: CanvasRenderingContext2D;
private canvasWidth = 160;
private canvasHeight = 90;
private interferenceHeight = 50;
private lastFrameUpdate: number = 0;
private frameInterval: number = 60;
private useAnimationFrame: boolean = !!window.requestAnimationFrame;
private noiseAnimationWindowPos: number;
private frameUpdateHandlerId: number;
constructor(config: ComponentConfig = {}) {
super(config);
this.config = this.mergeConfig(
config,
{
cssClass: 'ui-tvnoisecanvas',
},
this.config,
);
}
protected toDomElement(): DOM {
return (this.canvas = new DOM('canvas', { class: this.getCssClasses() }, this));
}
start(): void {
this.canvasElement = <HTMLCanvasElement>this.canvas.get(0);
this.canvasContext = this.canvasElement.getContext('2d');
this.noiseAnimationWindowPos = -this.canvasHeight;
this.lastFrameUpdate = 0;
this.canvasElement.width = this.canvasWidth;
this.canvasElement.height = this.canvasHeight;
this.renderFrame();
}
stop(): void {
if (this.useAnimationFrame) {
cancelAnimationFrame(this.frameUpdateHandlerId);
} else {
clearTimeout(this.frameUpdateHandlerId);
}
}
private renderFrame(): void {
// This code has been copied from the player controls.js and simplified
if (this.lastFrameUpdate + this.frameInterval > new Date().getTime()) {
// It's too early to render the next frame
this.scheduleNextRender();
return;
}
let currentPixelOffset;
const canvasWidth = this.canvasWidth;
const canvasHeight = this.canvasHeight;
// Create texture
const noiseImage = this.canvasContext.createImageData(canvasWidth, canvasHeight);
// Fill texture with noise
for (let y = 0; y < canvasHeight; y++) {
for (let x = 0; x < canvasWidth; x++) {
currentPixelOffset = canvasWidth * y * 4 + x * 4;
noiseImage.data[currentPixelOffset] = Math.random() * 255;
if (y < this.noiseAnimationWindowPos || y > this.noiseAnimationWindowPos + this.interferenceHeight) {
noiseImage.data[currentPixelOffset] *= 0.85;
}
noiseImage.data[currentPixelOffset + 1] = noiseImage.data[currentPixelOffset];
noiseImage.data[currentPixelOffset + 2] = noiseImage.data[currentPixelOffset];
noiseImage.data[currentPixelOffset + 3] = 50;
}
}
// Put texture onto canvas
this.canvasContext.putImageData(noiseImage, 0, 0);
this.lastFrameUpdate = new Date().getTime();
this.noiseAnimationWindowPos += 7;
if (this.noiseAnimationWindowPos > canvasHeight) {
this.noiseAnimationWindowPos = -canvasHeight;
}
this.scheduleNextRender();
}
private scheduleNextRender(): void {
if (this.useAnimationFrame) {
this.frameUpdateHandlerId = window.requestAnimationFrame(this.renderFrame.bind(this));
} else {
this.frameUpdateHandlerId = window.setTimeout(this.renderFrame.bind(this), this.frameInterval);
}
}
}