1/* 2 * Copyright 2024 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import { Timeline } from './timeline'; 18 19/** Translates pixel values to frames / video timestamps, and vice-versa. */ 20export class VisualTimeline extends EventTarget { 21 constructor( 22 width: number, 23 public readonly timeline: Timeline, 24 ) { 25 super(); 26 this._width = width; 27 } 28 29 private _width: number; 30 31 get width(): number { 32 return this._width; 33 } 34 35 /** 36 * Pixel-width of the interaction area. 37 * 38 * `0`px ≙ `0`s, while `width`px ≙ the duration. 39 */ 40 set width(value: number) { 41 if (value == this._width) return; 42 this._width = value; 43 this.dispatchEvent(new Event('timeline-changed')); 44 } 45 46 /** Pixel-value that marks the given time. */ 47 timeToPx(timeSeconds: number): number { 48 if (timeSeconds < 0) return Number.NEGATIVE_INFINITY; 49 const duration = this.timeline.duration; 50 if (timeSeconds > duration) return Number.POSITIVE_INFINITY; 51 52 return this.width * (timeSeconds / duration); 53 } 54 55 /** Pixel-value that marks the given time. */ 56 timeToPxClamped(timeSeconds: number): number { 57 if (timeSeconds < 0) return Number.NEGATIVE_INFINITY; 58 const duration = this.timeline.duration; 59 if (timeSeconds > duration) return Number.POSITIVE_INFINITY; 60 61 return ( 62 this.width * (this.timeline.clampTimeToFrame(timeSeconds) / duration) 63 ); 64 } 65 66 /** Pixel-value that marks the beginning of the given frame. */ 67 frameToPx(frame: number): number { 68 return this.timeToPx(this.timeline.frameToTime(frame)); 69 } 70 71 /** Video-time associated with the given pixel value. */ 72 pxToTime(px: number): number { 73 if (px < 0) return Number.NEGATIVE_INFINITY; 74 const width = this.width; 75 if (px > width) return Number.POSITIVE_INFINITY; 76 77 return this.timeline.duration * (px / width); 78 } 79 80 /** Frame number associated with the given pixel value. */ 81 pxToFrame(px: number): number { 82 return this.timeline.timeToFrame(this.pxToTime(px)); 83 } 84} 85