Estoy tratando de crear una aplicación donde la línea sigue a la y de mi mouse y se desplaza hacia la izquierda cada x ms.
El problema es que hay artefactos en las uniones de cada línea, como en la imagen de abajo.
¿Cómo puedo prevenirlo?
Y potencialmente suavizar las líneas. La parte principal de la pregunta es cómo evitar que se rompa, pero si tienes una idea de cómo suavizar esta línea, házmelo saber.
let y = null const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); ctx.strokeStyle = 'red'; ctx.lineCap = 'round'; ctx.lineJoin = 'round' ctx.lineWidth = 5; const width = ctx.canvas.width const height = ctx.canvas.height let prevY = null; const onMouseUpdate = (e) => { y = e.pageY; } const shift = 10 canvas.addEventListener('mousemove', onMouseUpdate, false); canvas.addEventListener('mouseenter', onMouseUpdate, false); function draw() { const rect = canvas.getBoundingClientRect() const yTarget = y - rect.top ctx.beginPath() ctx.moveTo(width - shift, prevY); ctx.lineTo(width, yTarget); ctx.stroke(); prevY = yTarget const imageData = ctx.getImageData(shift, 0, width - shift, height); ctx.putImageData(imageData, 0, 0); ctx.clearRect(width - shift, 0, shift, height); } setInterval(draw, 300)La mejor opción para esto es dibujar una ruta única: almacena todos sus puntos en una matriz, luego, en cada cuadro nuevo, borra todo el contexto y traza la ruta completa nuevamente.
Aquí hay un código de muestra donde elimino de la matriz los puntos que quedaron fuera de la pantalla. Tenga en cuenta que también utilizo requestAnimationFrame() , que programa una devolución de llamada para que se active en el siguiente cuadro de pintura, lo que permite animaciones fluidas.
const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); let y = canvas.height / 2; let data = []; ctx.strokeStyle = 'red'; ctx.lineCap = 'round'; ctx.lineJoin = 'round' ctx.lineWidth = 5; const onMouseUpdate = (e) => { const rect = canvas.getBoundingClientRect(); const yTarget = y - rect.top; y = e.pageY; } canvas.addEventListener('mousemove', onMouseUpdate, false); canvas.addEventListener('mouseenter', onMouseUpdate, false); let lastTime = performance.now(); const speed = 33.3; // 10px per 300ms -> 33.3px per s requestAnimationFrame(loop); function loop(now) { // calculate x based on how much time elapsed since the last frame // we store the distance from the last point const x = ((now - lastTime) / 1000) * speed; lastTime = now; data.push({x, y}); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.beginPath() let currentX = canvas.width; // iterate from end to start for (let i = data.length - 1; i >= 0; i--) { const {x, y} = data[i]; currentX -= data[i].x; if (currentX < 0) { // the first points are outside the screen data = data.slice(i); // update our data array break; } ctx.lineTo(currentX, y); } // paint all in one call ctx.stroke(); requestAnimationFrame(loop); } <canvas id="canvas" height="400" width="500"> </canvas>El problema es que estás dibujando hasta el borde del lienzo. Entonces, la parte final redondeada que vale lineWidth/2 se dibuja fuera del lienzo y se recorta. Debe dibujar hasta canvasWidth - lineWidth/2 solamente:
let y = null; // resolution of the canvas const scale = 2; const lineWidth = 6; const shift = 7; const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); canvas.width = canvas.clientWidth * scale; canvas.height = canvas.clientHeight * scale; ctx.strokeStyle = 'red'; ctx.lineCap = 'round'; ctx.lineJoin = 'round' ctx.lineWidth = lineWidth; const width = ctx.canvas.width - lineWidth / 2; const height = ctx.canvas.height const rect = canvas.getBoundingClientRect(); let prevY = null; const onMouseUpdate = (e) => { y = (e.clientY - rect.top) * scale; } canvas.addEventListener('mousemove', onMouseUpdate, false); canvas.addEventListener('mouseenter', onMouseUpdate, false); let xOff = 0 function draw() { const yTarget = y; ctx.beginPath() ctx.moveTo(width - shift, prevY); ctx.lineTo(width, yTarget); ctx.stroke(); prevY = yTarget const imageData = ctx.getImageData(shift, 0, width + +20 + lineWidth / 2, height); ctx.clearRect(0, 0, rect.width, rect.height); ctx.putImageData(imageData, 0, 0); } let i = setInterval(draw, 200); canvas { height: 90vh; border: 1px solid blue; width: 80vw; } <canvas id="canvas"> </canvas>He aumentado la resolución del lienzo. Para obtener más información, consulte esta discusión .