JSFiddle - React, Tailwind, and code Playground

by brigand

HTML

<main>
  <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloribus aspernatur reprehenderit illo, ducimus, quae expedita quas adipisci, laboriosam culpa quos reiciendis similique minima quia tempora corrupti in esse repudiandae cum.</div>
  <div>Maxime laboriosam facere rerum eaque, eos debitis eveniet saepe commodi atque explicabo tempora ad voluptate nisi, soluta deleniti modi neque delectus, sunt vel cupiditate. Optio voluptas autem deleniti sed quasi.</div>
  <div>Nostrum eum consequuntur sint accusamus alias, quibusdam deleniti, dicta unde et eligendi officiis minus fugiat, commodi maiores, iure iste quo? Sequi, aspernatur, vitae illo qui nesciunt id explicabo voluptatum. Commodi!</div>
  <div>Nostrum assumenda voluptas vero mollitia. Iure deleniti distinctio nesciunt cum expedita harum est molestias commodi, repudiandae aspernatur ut incidunt ipsum voluptatibus, maiores eaque nihil deserunt totam esse quam sed facere!</div>
  <div>Fugit vel quo impedit iusto sint quisquam esse eum excepturi tempore provident maxime accusantium maiores consequatur accusamus, praesentium. Sapiente, aspernatur amet voluptatum officia reiciendis optio quibusdam accusantium facere ad reprehenderit!</div>
  <div>Molestias laudantium labore ipsum dolorem aperiam vitae rerum? Quas enim aliquam quaerat ad corporis ducimus deserunt id expedita, neque dicta itaque distinctio ex, labore! Esse architecto ipsum error vel magni?</div>
  <div>Suscipit sapiente explicabo impedit molestias, aut, voluptate dolorem, sequi quos provident minus nemo soluta quae cumque maxime! Reiciendis cumque accusamus quos praesentium quisquam, doloremque, blanditiis libero omnis. Sed, blanditiis, unde.</div>
  <div>Recusandae nisi repellendus obcaecati eaque architecto velit officiis deserunt ea vitae. Inventore reprehenderit magni mollitia corporis, blanditiis nostrum sunt, officia, doloribus ratione fugit iste doloremque odio fuga saepe consectetur repellendus!</div>
  <div>Quisquam deserunt...

CSS

html,
body {
  margin: 0;
  padding: 0;
}

#overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
}

.drag::after {
  content: '';
  display: block;
  position: absolute;
  top: -100em;
  right: -100em;
  bottom: -100em;
  left: -100em;
}

JavaScript

const canvas = document.querySelector('#overlay');
canvas.width = Math.round(window.innerWidth * window.devicePixelRatio * 2);
canvas.height = Math.round(window.innerHeight * window.devicePixelRatio * 2);
const ctx = canvas.getContext('2d');

class HitTarget {
  constructor(opts, drawImpl) {
    this.drawImpl = drawImpl;
    this.x = 0;
    this.y = 0;
    this.width = 100;
    this.height = 100;
    this.events = {};
    this.canvas = canvas;
    this.ctx = ctx;

    Object.assign(this, opts);
    if (!this.el) {
      this.el = document.createElement('div');
      this.el.style.position = 'absolute';
    }
  }

  draw() {
    this.el.style.top = `${this.y}px`
    this.el.style.left = `${this.x}px`
    this.el.style.width = `${this.width}px`
    this.el.style.height = `${this.height}px`
    this.drawImpl(this);
  }

  init() {
    document.body.appendChild(this.el);

    for (const event of Object.keys(this.events)) {
      this.el.addEventListener(event, (ev) => {
        this.events[event](ev, this);
      })
    }
  }

  update() {

  }
}

const targets = [];

targets.push(new HitTarget({
  x: 10,
  y: 10,
  width: 200,
  height: 50,
  events: {
    mousedown: (ev, ht) => {
      ht.el.classList.toggle('drag', true)
      ht.drag = {
        x: ht.x,
        y: ht.y,
        startX: ev.pageX,
        startY: ev.pageY,
      }
    },
    mouseup: (ev, ht) => {
      ht.el.classList.toggle('drag', false)
      ht.drag = null
    },
    mousemove: (ev, ht) => {
      if (ht.drag) {
        ht.x = ht.drag.x + ev.pageX - ht.drag.startX
        ht.y = ht.drag.y + ev.pageY - ht.drag.startY
      }
    },
  }
}, (ht) => {
  ht.ctx.fillStyle = 'red';
  ht.ctx.fillRect(ht.x, ht.y , ht.width, ht.height)
}))

targets.forEach(target => target.init())

function run() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  targets.forEach(target => target.draw())
  requestAnimationFrame(run)
}
run();