JSFiddle - React, Tailwind, and code Playground

HTML

<html>
  <body>
    <div id="main"></div>
  </body>
</html>

TypeScript

/**
 * Una vez completada la carga de la página iniciamos el proceso de firma
 */
document.addEventListener('DOMContentLoaded', async () => {
const container = document.querySelector('div#main') as HTMLElement;

if (container) {
  // Prepara el contenedor
  container.innerHTML = '';
  container.style.height = '100vh';
  container.style.display = 'flex';
  container.style.alignItems = 'center';
  container.style.justifyContent = 'center';
  container.style.background = '#eee';
  container.style.overflow = 'hidden';
  container.style.touchAction = 'none';

  // Crea el canvas
  const canvas = document.createElement('canvas');
  canvas.width = 400;
  canvas.height = 300;
  canvas.style.border = '2px solid black';
  canvas.style.background = 'white';
  container.appendChild(canvas);

  const ctx = canvas.getContext('2d')!;
  ctx.lineWidth = 2;
  ctx.strokeStyle = 'blue';
  ctx.lineCap = 'round';

  let drawing = false;
  let lastX = 0;
  let lastY = 0;

  const getPos = (e: PointerEvent) => {
    const rect = canvas.getBoundingClientRect();
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top,
    };
  };

  canvas.addEventListener('pointerdown', e => {
    e.preventDefault();
    const pos = getPos(e);
    lastX = pos.x;
    lastY = pos.y;
    drawing = true;
  });

  canvas.addEventListener('pointermove', e => {
    if (!drawing) return;
    e.preventDefault();
    const pos = getPos(e);
    ctx.beginPath();
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(pos.x, pos.y);
    ctx.stroke();
    lastX = pos.x;
    lastY = pos.y;
  });

  canvas.addEventListener('pointerup', () => (drawing = false));
  canvas.addEventListener('pointerleave', () => (drawing = false));

  console.log('✅ Canvas listo. Si ves un rectángulo blanco con borde negro, dibuja con el dedo o ratón.');
} else {
  console.warn('No se encontró el div#main');
}

});