Mercurial > code > home > repos > light9
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 } |