Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

85
Views
Shift line without tearing its ends in html canvas

I am trying to create an application where the line follows y of my mouse and it's shifted to the left every x ms.

The problem is that there are artifacts on the joins of each line, like in the picture below.

How can I prevent it?

And potentially smooth the lines. The main part of the question is how to prevent tearing but if you have an idea of how to smooth this line let me know.

enter image description here

Code below or alternativly JsFiddle

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)

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

The best option for this is to draw a single path: You store all your points in an Array, then at every new frame you clear the whole context and trace the whole path again.

Here is a sample code where I remove from the Array the points that went out of screen. Note that I also make use of requestAnimationFrame() which schedules a callback to fire at the next painting frame, allowing for smooth animations.

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>

about 4 years ago · Juan Pablo Isaza Report

0

The issue is because you are drawing till the edge of the canvas. So the rounded end part worth lineWidth/2 gets drawn outside of the canvas and gets clipped. You need to draw till canvasWidth - lineWidth/2 only:

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>

I've increased the canvas resolution. For more info refer this discussion.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!