| 13 | |
| 14 | @nodeName('View2D') |
| 15 | export class View2D extends Rect { |
| 16 | // TODO: scope this to individual player |
| 17 | @lazy(() => { |
| 18 | const frameID = 'revideo-2d-frame'; |
| 19 | let frame = document.querySelector<HTMLDivElement>(`#${frameID}`); |
| 20 | if (!frame) { |
| 21 | frame = document.createElement('div'); |
| 22 | frame.id = frameID; |
| 23 | frame.style.position = 'absolute'; |
| 24 | frame.style.pointerEvents = 'none'; |
| 25 | frame.style.top = '0'; |
| 26 | frame.style.left = '0'; |
| 27 | frame.style.fontFeatureSettings = 'normal'; // TODO: find solution that fully isolates CSS |
| 28 | frame.style.opacity = '0'; |
| 29 | frame.style.overflow = 'hidden'; |
| 30 | document.body.prepend(frame); |
| 31 | } |
| 32 | return frame.shadowRoot ?? frame.attachShadow({mode: 'open'}); |
| 33 | }) |
| 34 | public static shadowRoot: ShadowRoot; |
| 35 | |
| 36 | @initial(PlaybackState.Paused) |
| 37 | @signal() |
| 38 | public declare readonly playbackState: SimpleSignal<PlaybackState, this>; |
| 39 | |
| 40 | @initial(0) |
| 41 | @signal() |
| 42 | public declare readonly globalTime: SimpleSignal<number, this>; |
| 43 | |
| 44 | @initial(0) |
| 45 | @signal() |
| 46 | public declare readonly fps: SimpleSignal<number, this>; |
| 47 | |
| 48 | @signal() |
| 49 | public declare readonly assetHash: SimpleSignal<string, this>; |
| 50 | |
| 51 | public constructor(props: View2DProps) { |
| 52 | super({ |
| 53 | composite: true, |
| 54 | fontFamily: 'Roboto', |
| 55 | fontSize: 48, |
| 56 | lineHeight: '120%', |
| 57 | textWrap: false, |
| 58 | fontStyle: 'normal', |
| 59 | ...props, |
| 60 | }); |
| 61 | this.view2D = this; |
| 62 | |
| 63 | View2D.shadowRoot.append(this.element); |
| 64 | this.applyFlex(); |
| 65 | } |
| 66 | |
| 67 | public override dispose() { |
| 68 | this.removeChildren(); |
| 69 | super.dispose(); |
| 70 | } |
| 71 | |
| 72 | public override async render(context: CanvasRenderingContext2D) { |