Estoy tratando de crear un bloc de dibujo muy básico usando un lienzo HTML y eventos de puntero. Implementé un codepen muy básico que demuestra el problema en cuestión. La funcionalidad clave dibuja un punto rojo en los eventos de pointerdown hacia abajo y un punto azul en pointermove :
c.addEventListener('pointerdown', event => { event.preventDefault(); updatePosition(event); ctx.fillStyle = 'red'; ctx.fillRect(position.x - 2, position.y - 2, 4, 4); drawing = true; }); c.addEventListener('pointermove', event => { if (!drawing) return; event.preventDefault(); updatePosition(event); ctx.fillStyle = 'blue'; ctx.fillRect(position.x - 2, position.y - 2, 4, 4); }); Tanto cuando uso un lápiz óptico (un lápiz para Surface) como cuando simplemente lo toco, observo una gran brecha entre el evento inicial del pointerdown y el "flujo" final (bueno) de eventos del movimiento del pointermove , como se demuestra aquí:
He observado en diversos grados con múltiples métodos de entrada en diferentes hardware. ¿Hay alguna manera de eliminar o mitigar esto?
Usar el método fillRect() no es la mejor opción para el dibujo interactivo 'en vivo'. La razón es que el evento pointermove o cualquier evento tiene una precisión finita. Es decir, si mueve el mouse lo suficientemente rápido, no reconocerá todas las posiciones en las que el puntero podría haber tocado durante el movimiento; podría ser solo la posición inicial y final.
El enfoque habitual para tal escenario es usar el método lineTo() en su lugar, ya que dibuja una línea desde la posición anterior a la actual obtenida, por ejemplo, por un evento pointermove. El grosor de dicha línea se puede controlar mediante la propiedad lineWidth .
Aquí está su ejemplo modificado:
const c = document.getElementById('board'); const compStyle = window.getComputedStyle(c); const width = parseInt(compStyle.getPropertyValue('width').replace('px', '')); const height = parseInt(compStyle.getPropertyValue('height').replace('px', '')); c.width = width; c.height = height; const ctx = c.getContext('2d'); ctx.lineWidth = 4; ctx.lineCap = 'round'; const position = { x: 0, y: 0 }; const updatePosition = (event) => { let rect = event.target.getBoundingClientRect(); position.x = (event.clientX - rect.left) * (width / rect.width); position.y = (event.clientY - rect.top) * (height / rect.height); } let drawing = false; c.addEventListener('pointerdown', event => { event.preventDefault(); updatePosition(event); ctx.beginPath(); ctx.strokeStyle = 'red'; ctx.moveTo(position.x, position.y) ctx.arc(position.x, position.y, ctx.lineWidth / 4, 0, 2 * Math.PI); ctx.stroke(); ctx.strokeStyle = 'blue'; drawing = true; }); c.addEventListener('pointermove', event => { if (!drawing) return; event.preventDefault(); updatePosition(event); ctx.lineTo(position.x, position.y); ctx.stroke(); }); c.addEventListener('pointerup', event => { ctx.closePath(); drawing = false; }); #board { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; touch-action: none; } <canvas id="board"></canvas>