JSFiddle - React, Tailwind, and code Playground

by Yurik Elmanov

HTML

<h4>Drag 1 or more shapes.</h4>
<button type="button" onClick="handleSaveImage()">Save Image</button>
<canvas id="canvas" width="1170" height="606"></canvas>
<canvas id="shadow-canvas" width="570" height="606"></canvas>

CSS

#shadow-canvas {
  display: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

JavaScript

// get canvas related references
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const BB = canvas.getBoundingClientRect();
const offsetX = BB.left;
const offsetY = BB.top;
const WIDTH = canvas.width;
const HEIGHT = canvas.height;

const handleSaveImage = () => {
  const shadowCanvas = document.getElementById('shadow-canvas');
  const sctx = shadowCanvas.getContext('2d');
  
  // Заменить на фоновую картинку
  sctx.fillStyle = '#312F42';
  sctx.beginPath();
  sctx.rect(0, 0, 570, 606);
  sctx.closePath();
  sctx.fill();
  
  rects.forEach(({ x, y, width, height, fill }) => {
    sctx.fillStyle = fill;
    sctx.beginPath();
    sctx.rect(x >= 570 ? x - 600 : x, y, width, height);
    sctx.closePath();
    sctx.fill();
  });
  
  const link = document.createElement('a');
  link.download = 'image.png';
  link.href = shadowCanvas.toDataURL();
  link.click();
};

// drag related variables
let dragok = false;
let startX;
let startY;

// an array of objects that define different rectangles
const rects = [];
rects.push({
  x: 75 - 15,
  y: 50 - 15,
  width: 30,
  height: 30,
  fill: "#444444",
  isDragging: false
});
rects.push({
  x: 75 - 25,
  y: 50 - 25,
  width: 30,
  height: 30,
  fill: "#ff550d",
  isDragging: false
});
rects.push({
  x: 75 - 35,
  y: 50 - 35,
  width: 30,
  height: 30,
  fill: "#800080",
  isDragging: false
});
rects.push({
  x: 75 - 45,
  y: 50 - 45,
  width: 30,
  height: 30,
  fill: "#0c64e8",
  isDragging: false
});

// clear the canvas
const clear = () => {
	ctx.clearRect(0, 0, WIDTH, HEIGHT);
}

// draw a single rect
const createFiled = (x, y, width, height, fill) => {
	ctx.fillStyle = fill;
  ctx.beginPath();
  ctx.rect(x, y, width, height);
  ctx.closePath();
  ctx.fill();
};

const createItemOnField = () => {
	rects.forEach(({ x, y, width, height, fill }) => {
  	ctx.fillStyle = fill;
    createFiled(x, y, width, height);
  });
};

// redraw the scene
const draw = () => {
  clear();
  createFiled(0,...