JSFiddle - React, Tailwind, and code Playground

by gabihp30

HTML

<img width="750" height="750" src="https://pixijs.io/examples/examples/assets/bg_rotate.jpg" id="screenshot" draggable="false">
<svg width="750" height="750" viewBox="0 0 750 750" id="draw" xmlns="http://www.w3.org/2000/svg">
    <rect id="marquee" x="450" y="420" width="150" height="150" />
    <g id="boxes"></g>
</svg>

CSS

#screenshot { 
  position:absolute;
  z-index:1;
  
}
#draw {
  position:absolute;
  z-index:2;
  pointer-events:none;
}
#draw #boxes {
  fill:blue;
  stroke:white;
  stroke-width:1;
  fill-opacity:0.5;
  stroke-opacity:0.9;
}
#draw #marquee {
  fill:white;
  stroke:red;
  stroke-width:1;
  stroke-dasharray:5,5;
  stroke-opacity:0.9;
  fill-opacity:0.2;
}
#draw #marquee.hide {
  display:none;
}

JavaScript

const $ = document.querySelector.bind(document);

/**
 * Collection of rectangles defining user generated regions
 */
const rectangles = [];

// DOM elements
const $screenshot = $('#screenshot');
const $draw = $('#draw');
const $marquee = $('#marquee');
const $boxes = $('#boxes');

// Temp variables
let startX = 0;
let startY = 0;
const marqueeRect = {
	x: 0,
  y: 0,
  width: 0,
  height: 0,
};

$marquee.classList.add('hide');
$screenshot.addEventListener('pointerdown', startDrag);

function startDrag(ev) {
	// middle button delete rect
	if (ev.button === 1) {
  	const rect = hitTest(ev.layerX, ev.layerY);
    if (rect) {
    	rectangles.splice(rectangles.indexOf(rect), 1);
      redraw();
    }
  	return;
  }
	window.addEventListener('pointerup', stopDrag);
  $screenshot.addEventListener('pointermove', moveDrag);
  $marquee.classList.remove('hide');
  startX = ev.layerX;
  startY = ev.layerY;
  drawRect($marquee, startX, startY, 0, 0);
}

function stopDrag(ev) {
  $marquee.classList.add('hide');
	window.removeEventListener('pointerup', stopDrag);
  $screenshot.removeEventListener('pointermove', moveDrag);
  if (ev.target === $screenshot && marqueeRect.width && marqueeRect.height) {
  	rectangles.push(Object.assign({}, marqueeRect));
  	redraw();
  }
}

function moveDrag(ev) {
	let x = ev.layerX;
  let y = ev.layerY;
	let width = startX - x;
  let height = startY - y;
  if (width < 0) {
  	width *= -1;
    x -= width;
  }
  if (height < 0) {
  	height *= -1;
    y -= height;
  }
  Object.assign(marqueeRect, { x, y, width, height });
  drawRect($marquee, marqueeRect);
}

function hitTest(x, y) {
	return rectangles.find(rect => (
  	x >= rect.x &&
    y >= rect.y && 
    x <= rect.x + rect.width &&
    y <= rect.y + rect.height
  ));
}

function redraw() {
	boxes.innerHTML = '';
  rectangles.forEach((data) => {
    boxes.appendChild(drawRect(
    	document.createElementNS("http://www.w3.org/2000/svg", 'rect'), data
    ));
  });
}

function drawRect(rect, data)...