JSFiddle - React, Tailwind, and code Playground

by Lazaro Contreras

HTML

<script src="https://cdn.tailwindcss.com"></script>
<div class="flex flex-col h-screen">
  <header class="bg-blue-600 py-2">
    <h1 class="text-white text-center text-2xl font-bold">Canvas Drawing</h1>
  </header>
  <main class="flex-grow flex justify-center items-center">
    <div class="relative rounded-md border border-gray-300 shadow-lg overflow-hidden">
      <canvas id="canvas" class="w-96 h-96 sm:w-128 sm:h-128"></canvas>
      <div class="absolute top-0 left-0 p-2">
        <input type="color" id="colorPicker" class="h-10 w-10 rounded-full border border-gray-300">
      </div>
    </div>
  </main>
  <footer class="bg-gray-100 py-2">
    <div class="container mx-auto flex justify-between px-4">
      <div class="flex items-center space-x-2">
        <button class="shapeBtn active" data-shape="line">
          <svg class="w-6 h-6 stroke-current text-gray-700" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
          </svg>
        </button>
        <button class="shapeBtn" data-shape="square">
          <svg class="w-6 h-6 stroke-current text-gray-700" viewBox="0 0 24 24">
            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
          </svg>
        </button>
        <button class="shapeBtn" data-shape="circle">
          <svg class="w-6 h-6 stroke-current text-gray-700" viewBox="0 0 24 24">
            <circle cx="12" cy="12" r="9"></circle>
          </svg>
        </button>
      </div>
      <button id="clearBtn" class="bg-white hover:bg-gray-200 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow">
        Clear
      </button>
    </div>
  </footer>
</div>

CSS

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');

body {
  font-family: 'Inter', sans-serif;
}

input[type="color"]::-webkit-color-swatch {
  border: none;
  border-radius: 50%;
}

input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}

input[type="color"]::-moz-color-swatch {
  border: none;
  border-radius: 50%;
}

input[type="color"]::-moz-color-swatch-wrapper {
  padding: 0;
}

@media screen and (min-width: 640px) {
  canvas {
    width: 128px;
    height: 128px;
  }
}

JavaScript

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

const colorPicker = document.getElementById('colorPicker');
const shapeBtns = document.querySelectorAll('.shapeBtn');
const clearBtn = document.getElementById('clearBtn');

let startX, startY;
function draw(x, y) {
  ctx.lineTo(x, y);
  ctx.stroke();
}

function drawRect(x, y, width, height) {
  ctx.fillRect(x, y, width, height);
}

function drawCircle(x, y, radius) {
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, 2 * Math.PI);
  ctx.fill();
}

function startDrawing(event) {
  startX = event.clientX - canvas.offsetLeft;
  startY = event.clientY - canvas.offsetTop;
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  canvas.addEventListener('mousemove', continueDrawing);
}

function continueDrawing(event) {
  const currentX = event.clientX - canvas.offsetLeft;
  const currentY = event.clientY - canvas.offsetTop;
  draw(currentX, currentY);
}

function stopDrawing() {
  canvas.removeEventListener('mousemove', continueDrawing);
}

function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

function changeColor(event) {
  ctx.fillStyle = event.target.value;
  ctx.strokeStyle = event.target.value;
}

function changeShape(event) {
  const shape = event.target.dataset.shape;
  if (shape === 'line') {
    canvas.removeEventListener('mousedown', startDrawing);
    canvas.removeEventListener('mouseup', stopDrawing);
    canvas.removeEventListener('mouseout', stopDrawing);
    canvas.addEventListener('mousedown', startDrawing);
    canvas.addEventListener('mouseup', stopDrawing);
    canvas.addEventListener('mouseout', stopDrawing);
  } else if (shape === 'square') {
    canvas.removeEventListener('mousedown', startDrawing);
    canvas.removeEventListener('mouseup', stopDrawing);
    canvas.removeEventListener('mouseout', stopDrawing);
    canvas.addEventListener('mousedown', function(event) {
      startX = event.clientX - canvas.offsetLeft;
      startY = event.clientY...