JSFiddle - React, Tailwind, and code Playground

by Rajesh Danabal

HTML

<canvas id="canvas" width="800" height="600" style="border:1px solid #ccc;"></canvas>

JavaScript

class ViewTransform {
  constructor() {
    this.scale = 1;
    this.offsetX = 0;
    this.offsetY = 0;
  }

  transformX(x) {
    return x * this.scale + this.offsetX;
  }

  transformY(y) {
    return y * this.scale + this.offsetY;
  }

  inverseX(screenX) {
    return (screenX - this.offsetX) / this.scale;
  }

  inverseY(screenY) {
    return (screenY - this.offsetY) / this.scale;
  }

  pan(dx, dy) {
    this.offsetX += dx;
    this.offsetY += dy;
  }

  zoomAt(mouseX, mouseY, zoomFactor) {
    const worldX = this.inverseX(mouseX);
    const worldY = this.inverseY(mouseY);
    this.scale *= zoomFactor;
    this.offsetX = mouseX - worldX * this.scale;
    this.offsetY = mouseY - worldY * this.scale;
  }
}

class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class Rect extends Shape {
  constructor(x, y, w, h, color = 'blue') {
    super(x, y);
    this.w = w;
    this.h = h;
    this.color = color;
  }

  draw(ctx, vt) {
    ctx.fillStyle = this.color;
    ctx.fillRect(
      vt.transformX(this.x),
      vt.transformY(this.y),
      this.w * vt.scale,
      this.h * vt.scale
    );
  }
}

class Line {
  constructor(x1, y1, x2, y2, color = 'black') {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.color = color;
  }

  draw(ctx, vt) {
    ctx.strokeStyle = this.color;
    ctx.beginPath();
    ctx.moveTo(vt.transformX(this.x1), vt.transformY(this.y1));
    ctx.lineTo(vt.transformX(this.x2), vt.transformY(this.y2));
    ctx.stroke();
  }
}

class Layer {
  constructor() {
    this.shapes = [];
  }

  add(shape) {
    this.shapes.push(shape);
  }

  draw(ctx, vt) {
    this.shapes.forEach(shape => shape.draw(ctx, vt));
  }
}

// Init
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const vt = new ViewTransform();
const layers = [];

// Sample Layers
const rectLayer = new Layer();
rectLayer.add(new Rect(100, 100, 80, 50, 'tomato'));
rectLayer.add(new Rect(300, 200, 100,...