Solo
Solo

Reputation: 6957

Reusable canvas code?

I need to use several canvas-es with different values (see data-percent) with same reusable code block but "animation" makes it a little bit tricky. Im not sure how to make it reusable. Copy-pasting the same code over and over again is obviously a wrong move, I usually avoid it at any cost.


First thing is obviously to remove id and use class instead, then I could select all the canvas-es:

<canvas class="circle-thingy" width="120" height="120" data-percent="75"></canvas>
<canvas class="circle-thingy" width="120" height="120" data-percent="23"></canvas>
<canvas class="circle-thingy" width="120" height="120" data-percent="89"></canvas>

var allCircles = document.getElementsByClassName('circle-thingy');

But now comes the trickier part.. How about canvas JavaScript code? There's probably a very easy solution but I can't see it! Terrible time to quit smoking I guess (as always), brain is like shut down.


What I tried: for loop with allCircles list. Problem is that I cannot use setInterval and clearTimeout with this approach. Dynamic variable names? How do I reference them later?


Here's my code with a single circle, try it.

// Get canvas context
var ctx = document.getElementById('my-circle').getContext('2d');
// Current percent
var currentPercent = 0;
// Canvas north (close enough)
var start = 4.72;
// Dimensions
var cWidth = ctx.canvas.width;
var cHeight = ctx.canvas.height;
// Desired percent -> comes from canvas data-* attribute
var finalPercent = ctx.canvas.getAttribute('data-percent');
var diff;

function circle() {
  diff = ((currentPercent / 100) * Math.PI * 2 * 10).toFixed(2);
  ctx.clearRect(0, 0, cWidth, cHeight);
  ctx.lineWidth = 3;
  
  // Bottom circle (grey)
  ctx.strokeStyle = '#eee';
  ctx.beginPath();
  ctx.arc(60, 60, 55, 0, 2 * Math.PI);
  ctx.stroke();

  // Percent text
  ctx.fillStyle = '#000';
  ctx.textAlign = 'center';
  ctx.font="900 10px arial";
  ctx.fillText(currentPercent + '%', cWidth * 0.5, cHeight * 0.5 + 2, cWidth);

  // Upper circle (blue)
  ctx.strokeStyle = '#0095ff';
  ctx.beginPath();
  ctx.arc(60, 60, 55, start, diff / 10 + start);
  ctx.stroke();

  // If has desired percent -> stop
  if( currentPercent >= finalPercent) {
    clearTimeout(myCircle);
  }
  currentPercent++;
}

var myCircle = setInterval(circle, 20);
<canvas id="my-circle" width="120" height="120" data-percent="75"></canvas>

Feel free to use this code snippet in your own projects.

Upvotes: 0

Views: 64

Answers (1)

user3707125
user3707125

Reputation: 3484

You can use bind to solve this.

Create a helper function that will start animation for given canvas:

function animateCircle(canvas) {
    var scope = {
        ctx: canvas.getContext('2d')
        // other properties, like currentPercent, finalPercent, etc
    };        
    scope.interval = setInterval(circle.bind(scope), 20);
}

Change your circle function to refer variables from this instead of global ones:

function circle() {
    // your old code with corresponding changes
    // e.g.
    var ctx = this.ctx; // references corresponding scope.ctx
    // or
    this.currentPercent++; // references corresponding scope.currentPercent
}

Working JSFiddle, if something is not clear.

Upvotes: 1

Related Questions