Canvas Drag Resize Shapes

by northcoder

HTML

<!doctype html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <title>Canvas Example</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  </head>

  <body>

    <img id="source" alt="taverna" style="display: none;" src="https://user-images.githubusercontent.com/56733719/141156687-8bbb9eec-4752-4169-b108-50e397e4a8b7.png">

    <div class="imgmap_centered">
      <button id="rect" class="imgmap_shapes">New &#11036;</button>
      <button id="circle" class="imgmap_shapes">New &#9711;</button>
    </div>

    <div id="container" style="margin: auto; border:1px solid #d3d3d3;">
      <canvas id="canvas"></canvas>
    </div>

    <div class="imgmap" style="padding: 10px;">
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Shape</th>
            <th>Coordinates</th>
            <th></th>
          </tr>
        </thead>
        <tbody id="shapesRows">
        </tbody>
      </table>
    </div>

  </body>

</html>

CSS

imgmap_centered {
  text-align: center;
}

button.imgmap_shapes {
  display: inline-block;
  padding: 5px;
  margin: 10px;
}

div.imgmap table {
  margin: 5px auto;
  border-collapse: collapse;
  width: 50%;
}

div.imgmap th {
  text-align: left;
}

div.imgmap tr {
  border-bottom: 1px solid #ccc;
}

div.imgmap table td>button {
  padding: 5px;
  margin: 5px;
}

JavaScript

window.onload = function() {

  // inspired by https://stackoverflow.com/a/28285879/12567365
  // extended to support resizing, handling of one shape at
  // a time, scrolling/resizing for the viewport, and deletion
  // of shapes.

  // load image onto canvas
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  const img = document.getElementById('source');
  const container = document.getElementById('container');
  container.style.width = '' + (img.naturalWidth + 2) + 'px';
  canvas.width = img.naturalWidth;
  canvas.height = img.naturalHeight;
  ctx.drawImage(img, 1, 1);

  // get canvas related references
  const WIDTH = canvas.width;
  const HEIGHT = canvas.height;

  // an array of objects that define different shapes:
  var shapes = [];
  // each shape has one handle, for resizing the shape:
  var handles = [];

  // indexes into shapes[] and handles[]
  var dragIdx = -1;
  var resizeIdx = -1;

  //drag/resize related variables
  var startX;
  var startY;

  let fillStyle = 'rgba(255, 255, 255, 0.6)';
  let strokeStyle = 'red';

  $('button#rect').on('click', function() {
    drawNewRect();
  })

  $('button#circle').on('click', function() {
    drawNewCircle();
  })

  $('#shapesRows').on('click', 'button', function() {
    deleteShape(this);
  })

  // listen for global mouse events
  canvas.onmousedown = myDown;
  canvas.onmouseup = myUp;
  canvas.onmousemove = myMove;

  // call to draw the scene
  draw();


  function drawNewRect() {
    let newIdx = shapes.length;
    // define a rectangle
    shapes.push({
      idx: newIdx,
      type: "rect",
      x: 10,
      y: 10,
      width: 50,
      height: 40,
      fill: fillStyle,
      stroke: strokeStyle
    });

    var s = shapes[newIdx];
    handles.push({
      idx: newIdx,
      type: "rect",
      x: s.x + s.width - 5,
      y: s.y + s.height - 5,
      width: 5,
      height: 5,
      fill: fillStyle,
      stroke: strokeStyle
    });

    draw();
   ...