Mercurial > code > home > repos > light9
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/Light9CursorCanvas.ts Sun May 12 19:02:10 2024 -0700 @@ -0,0 +1,146 @@ +import debug from "debug"; +import { css, html, LitElement, PropertyValues } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import Sylvester from "sylvester"; +import { line } from "./drawing"; + +const $V = Sylvester.Vector.create; + +const log = debug("cursor"); + +export interface PlainViewState { + zoomSpec: { t1: () => number; t2: () => number }; + fullZoomX: (t: number) => number; + zoomInX: (t: number) => number; + cursor: { t: () => number }; + audioY: () => number; + audioH: () => number; + zoomedTimeY: () => number; // not what you think- it's the zone in between + zoomedTimeH: () => number; + mouse: { pos: () => Vector }; +} + +// For cases where you have a zoomed-out view on top of a zoomed-in view, +// overlay this element and it'll draw a time cursor on both views. +@customElement("light9-cursor-canvas") +export class Light9CursorCanvas extends LitElement { + cursorPath: null | { + top0: Vector; + top1: Vector; + mid0: Vector; + mid1: Vector; + mid2: Vector; + mid3: Vector; + bot0: Vector; + bot1: Vector; + } = null; + canvasEl!: HTMLCanvasElement; + ctx!: CanvasRenderingContext2D; + offsetWidth: any; + offsetHeight: any; + @property() viewState: PlainViewState | null = null; + static styles = [ + css` + :host { + display: inline-block; + } + `, + ]; + render() { + return html`<canvas></canvas>`; + } + + updated(changedProperties: PropertyValues) { + if (changedProperties.has("viewState")) { + this.redrawCursor(); + } + } + connectedCallback() { + super.connectedCallback(); + window.addEventListener("resize", this.onResize); + this.onResize(); + } + + firstUpdated() { + this.canvasEl = this.shadowRoot!.firstElementChild as HTMLCanvasElement; + this.onResize(); + this.ctx = this.canvasEl.getContext("2d")!; + } + + disconnectedCallback() { + window.removeEventListener("resize", this.onResize); + super.disconnectedCallback(); + } + + // onViewState() { + // ko.computed(this.redrawCursor.bind(this)); + // } + + onResize() { + if (!this.canvasEl) { + return; + } + this.canvasEl.width = this.offsetWidth; + this.canvasEl.height = this.offsetHeight; + this.redrawCursor(); + } + + redrawCursor() { + const vs = this.viewState; + if (!vs) { + return; + } + const dependOn = [vs.zoomSpec.t1(), vs.zoomSpec.t2()]; + const xZoomedOut = vs.fullZoomX(vs.cursor.t()); + const xZoomedIn = vs.zoomInX(vs.cursor.t()); + + this.cursorPath = { + top0: $V([xZoomedOut, vs.audioY()]), + top1: $V([xZoomedOut, vs.audioY() + vs.audioH()]), + mid0: $V([xZoomedIn + 2, vs.zoomedTimeY() + vs.zoomedTimeH()]), + mid1: $V([xZoomedIn - 2, vs.zoomedTimeY() + vs.zoomedTimeH()]), + mid2: $V([xZoomedOut - 1, vs.audioY() + vs.audioH()]), + mid3: $V([xZoomedOut + 1, vs.audioY() + vs.audioH()]), + bot0: $V([xZoomedIn, vs.zoomedTimeY() + vs.zoomedTimeH()]), + bot1: $V([xZoomedIn, this.offsetHeight]), + }; + this.redraw(); + } + + redraw() { + if (!this.ctx || !this.viewState) { + return; + } + this.ctx.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height); + + this.ctx.strokeStyle = "#fff"; + this.ctx.lineWidth = 0.5; + this.ctx.beginPath(); + const mouse = this.viewState.mouse.pos(); + line(this.ctx, $V([0, mouse.e(2)]), $V([this.canvasEl.width, mouse.e(2)])); + line(this.ctx, $V([mouse.e(1), 0]), $V([mouse.e(1), this.canvasEl.height])); + this.ctx.stroke(); + + if (this.cursorPath) { + this.ctx.strokeStyle = "#ff0303"; + this.ctx.lineWidth = 1.5; + this.ctx.beginPath(); + line(this.ctx, this.cursorPath.top0, this.cursorPath.top1); + this.ctx.stroke(); + + this.ctx.fillStyle = "#9c0303"; + this.ctx.beginPath(); + this.ctx.moveTo(this.cursorPath.mid0.e(1), this.cursorPath.mid0.e(2)); + for (let p of [this.cursorPath.mid1, this.cursorPath.mid2, this.cursorPath.mid3]) { + this.ctx.lineTo(p.e(1), p.e(2)); + } + this.ctx.fill(); + + this.ctx.strokeStyle = "#ff0303"; + this.ctx.lineWidth = 3; + this.ctx.beginPath(); + line(this.ctx, this.cursorPath.bot0, this.cursorPath.bot1, "#ff0303", "3px"); + this.ctx.stroke(); + } + } +}