Files @ e7e03c203c99
Branch filter:

Location: light9/web/Light9CursorCanvas.ts

drewp@bigasterisk.com
resize cursor canvas for 400px tall spectros. fix canvas resolution code
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";
import { Vector } from "./lib/sylvester";

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 };
}

export interface PlainerViewState {
  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: PlainerViewState | null = null;
  static styles = [
    css`
      :host {
        display: inline-block;
      }
      canvas {
        width: 100%;
        height: 100%;
        
      }
    `,
  ];
  resizeObserver!: ResizeObserver;
  render() {
    return html`<canvas></canvas>`;
  }

  updated(changedProperties: PropertyValues) {
    if (changedProperties.has("viewState")) {
      this.redrawCursor();
    }
  }
  connectedCallback() {
    super.connectedCallback();
    this.resizeObserver = new ResizeObserver(this.onResize.bind(this));
    this.resizeObserver.observe(this);
  }

  firstUpdated() {
    this.canvasEl = this.shadowRoot!.firstElementChild as HTMLCanvasElement;
    this.ctx = this.canvasEl.getContext("2d")!;
  }

  disconnectedCallback() {
    this.resizeObserver.unobserve(this);
  }

  onResize() {
    log('onResize', this.clientWidth, this.clientHeight);
    if (!this.canvasEl) {
      return;
    }
    this.canvasEl.width = this.clientWidth;
    this.canvasEl.height = this.clientHeight;
    this.redrawCursor();
  }

  redrawCursor() {
    const vs = this.viewState;
    if (!vs) {
      return;
    }
    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();
    }
  }
}