Rabindra
Rabindra

Reputation: 33

Canvas get pixelate after adding animation

After adding the animation, the canvas gets pixelated. I've tried to fix this with adding context.clearRect(0, 0, canvas.width, canvas.height); but it hides the previous segments

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = x - 40;
var endPercent = 45;
var curPerc = 0,
  mybeg;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
var col = ['#000', '#ff0000', '#002bff'];

function animate(current, colr, mybeg) {
  context.beginPath();
  context.moveTo(x, y);
  context.arc(x, y, radius, mybeg, ((circ) * current));
  context.fillStyle = colr;
  context.fill();
  //console.log(x, y, radius, mybeg, ((circ) * current));

  curPerc++;
  if (curPerc <= endPercent) {
    mybeg = 0;
    requestAnimationFrame(function() {
      animate(curPerc / 100, col[0], mybeg)
    });
  } else if (curPerc > 44 && curPerc <= 65) {
    const mybeg1 = ((circ) * 45 / 100);
    requestAnimationFrame(function() {
      animate(curPerc / 100, col[1], mybeg1)
    });
  } else if (curPerc > 64 && curPerc <= 100) {
    const mybeg2 = ((circ) * 65 / 100);
    requestAnimationFrame(function() {
      animate(curPerc / 100, col[2], mybeg2)
    });
  }
}
animate();
<canvas id="myCanvas" height="300" width="300"></canvas>

result

Upvotes: 0

Views: 40

Answers (1)

Kaiido
Kaiido

Reputation: 136678

You are redrawing the same arc over itself a lot of times. To render a smooth arc, we need semi-transparent pixels (antialiasing), and drawing semi-transparent pixels over other semi-transparent pixels will make them more an more opaque.

So the solution here is to clear everything and redraw everything at every frame.

There are several ways to do it, but one of the simplest might be to render your complete pie every-time and only animate a mask over it, using compositing:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = x - 40;

var stops = [
//[ begin, end , color ]  
  [  0,  45, '#000' ],
  [ 45,  65, '#ff0000' ],
  [ 65, 100, '#002bff' ]
];
var current = 0;

animate();

function drawFullPie() {
  stops.forEach( function( stop , i) {
    var begin = (stop[0] / 100 ) * Math.PI * 2;
    var end   = (stop[1] / 100 ) * Math.PI * 2;
    context.beginPath();
    context.moveTo( x, y );
    context.arc( x, y, radius, begin, end );
    context.fillStyle = stop[2];
    context.fill();
  } );
}

function drawMask() {
  var begin = 0;
  var end = (current / 100) * Math.PI * 2;
  // Keep whatever is currently painted to the canvas
  // only where our next drawings will appear
  context.globalCompositeOperation = 'destination-in';
  context.beginPath();
  context.moveTo( x, y );
  context.arc( x, y, radius, begin, end );
  context.fill();
  // disable masking
  context.globalCompositeOperation = 'source-over';
}


function animate() {
  // clear at every frame
  context.clearRect( 0, 0, canvas.width, canvas.height );
  // draw the full pie
  drawFullPie();
  // mask it as needed
  drawMask();
  // until complete
  if( current ++ < 100 ) {
    // do it again
    requestAnimationFrame( animate );
  }
}
<canvas id="myCanvas" height="300" width="300"></canvas>

Upvotes: 1

Related Questions