comparison web/Light9CursorCanvas.ts @ 2376:4556eebe5d73

topdir reorgs; let pdm have its src/ dir; separate vite area from light9/
author drewp@bigasterisk.com
date Sun, 12 May 2024 19:02:10 -0700
parents light9/web/Light9CursorCanvas.ts@e2ed5ce36253
children 06da5db2fafe
comparison
equal deleted inserted replaced
2375:623836db99af 2376:4556eebe5d73
1 import debug from "debug";
2 import { css, html, LitElement, PropertyValues } from "lit";
3 import { customElement, property } from "lit/decorators.js";
4 import Sylvester from "sylvester";
5 import { line } from "./drawing";
6
7 const $V = Sylvester.Vector.create;
8
9 const log = debug("cursor");
10
11 export interface PlainViewState {
12 zoomSpec: { t1: () => number; t2: () => number };
13 fullZoomX: (t: number) => number;
14 zoomInX: (t: number) => number;
15 cursor: { t: () => number };
16 audioY: () => number;
17 audioH: () => number;
18 zoomedTimeY: () => number; // not what you think- it's the zone in between
19 zoomedTimeH: () => number;
20 mouse: { pos: () => Vector };
21 }
22
23 // For cases where you have a zoomed-out view on top of a zoomed-in view,
24 // overlay this element and it'll draw a time cursor on both views.
25 @customElement("light9-cursor-canvas")
26 export class Light9CursorCanvas extends LitElement {
27 cursorPath: null | {
28 top0: Vector;
29 top1: Vector;
30 mid0: Vector;
31 mid1: Vector;
32 mid2: Vector;
33 mid3: Vector;
34 bot0: Vector;
35 bot1: Vector;
36 } = null;
37 canvasEl!: HTMLCanvasElement;
38 ctx!: CanvasRenderingContext2D;
39 offsetWidth: any;
40 offsetHeight: any;
41 @property() viewState: PlainViewState | null = null;
42 static styles = [
43 css`
44 :host {
45 display: inline-block;
46 }
47 `,
48 ];
49 render() {
50 return html`<canvas></canvas>`;
51 }
52
53 updated(changedProperties: PropertyValues) {
54 if (changedProperties.has("viewState")) {
55 this.redrawCursor();
56 }
57 }
58 connectedCallback() {
59 super.connectedCallback();
60 window.addEventListener("resize", this.onResize);
61 this.onResize();
62 }
63
64 firstUpdated() {
65 this.canvasEl = this.shadowRoot!.firstElementChild as HTMLCanvasElement;
66 this.onResize();
67 this.ctx = this.canvasEl.getContext("2d")!;
68 }
69
70 disconnectedCallback() {
71 window.removeEventListener("resize", this.onResize);
72 super.disconnectedCallback();
73 }
74
75 // onViewState() {
76 // ko.computed(this.redrawCursor.bind(this));
77 // }
78
79 onResize() {
80 if (!this.canvasEl) {
81 return;
82 }
83 this.canvasEl.width = this.offsetWidth;
84 this.canvasEl.height = this.offsetHeight;
85 this.redrawCursor();
86 }
87
88 redrawCursor() {
89 const vs = this.viewState;
90 if (!vs) {
91 return;
92 }
93 const dependOn = [vs.zoomSpec.t1(), vs.zoomSpec.t2()];
94 const xZoomedOut = vs.fullZoomX(vs.cursor.t());
95 const xZoomedIn = vs.zoomInX(vs.cursor.t());
96
97 this.cursorPath = {
98 top0: $V([xZoomedOut, vs.audioY()]),
99 top1: $V([xZoomedOut, vs.audioY() + vs.audioH()]),
100 mid0: $V([xZoomedIn + 2, vs.zoomedTimeY() + vs.zoomedTimeH()]),
101 mid1: $V([xZoomedIn - 2, vs.zoomedTimeY() + vs.zoomedTimeH()]),
102 mid2: $V([xZoomedOut - 1, vs.audioY() + vs.audioH()]),
103 mid3: $V([xZoomedOut + 1, vs.audioY() + vs.audioH()]),
104 bot0: $V([xZoomedIn, vs.zoomedTimeY() + vs.zoomedTimeH()]),
105 bot1: $V([xZoomedIn, this.offsetHeight]),
106 };
107 this.redraw();
108 }
109
110 redraw() {
111 if (!this.ctx || !this.viewState) {
112 return;
113 }
114 this.ctx.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);
115
116 this.ctx.strokeStyle = "#fff";
117 this.ctx.lineWidth = 0.5;
118 this.ctx.beginPath();
119 const mouse = this.viewState.mouse.pos();
120 line(this.ctx, $V([0, mouse.e(2)]), $V([this.canvasEl.width, mouse.e(2)]));
121 line(this.ctx, $V([mouse.e(1), 0]), $V([mouse.e(1), this.canvasEl.height]));
122 this.ctx.stroke();
123
124 if (this.cursorPath) {
125 this.ctx.strokeStyle = "#ff0303";
126 this.ctx.lineWidth = 1.5;
127 this.ctx.beginPath();
128 line(this.ctx, this.cursorPath.top0, this.cursorPath.top1);
129 this.ctx.stroke();
130
131 this.ctx.fillStyle = "#9c0303";
132 this.ctx.beginPath();
133 this.ctx.moveTo(this.cursorPath.mid0.e(1), this.cursorPath.mid0.e(2));
134 for (let p of [this.cursorPath.mid1, this.cursorPath.mid2, this.cursorPath.mid3]) {
135 this.ctx.lineTo(p.e(1), p.e(2));
136 }
137 this.ctx.fill();
138
139 this.ctx.strokeStyle = "#ff0303";
140 this.ctx.lineWidth = 3;
141 this.ctx.beginPath();
142 line(this.ctx, this.cursorPath.bot0, this.cursorPath.bot1, "#ff0303", "3px");
143 this.ctx.stroke();
144 }
145 }
146 }