Search code examples
javascriptanimationhtml5-canvas

Drawing a timed-cycle in a canvas


I have this code below which should draw cycle based on a timer. It, however, starts and draws the cycle in 3 simultaneous parts. I can't figure out why and how to get it to draw one smooth cycle:

window.requestAnimFrame = (function (callback) {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame || window.oRequestAnimationFrame || 
        window.msRequestAnimationFrame ||
        function (callback) {
            window.setTimeout(callback, 500 / 60);
        };
    })();

var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var data = ['Red,250', 'Blue,530', 'Orange,390', 'Green,190', 'Purple,450', 'Brown,600'];

var myCycle = {
    x: canvas.width / 2,
    y: (canvas.height / 2) - 200,//add to push down
    radius: 10,
    colour: '#8ED6FF'
};

//draw the object first, then animate it
var myTotal = 1810;
var lastend = -Math.PI / 2;
var currentEndAngle = 0;
var currentStartAngle = 0;
var radius = (canvas.height) / 6;

var startAngle = -1 * Math.PI/2;
var endAngle = startAngle + (Math.abs(190) / myTotal);


function drawArc(myVal, myCol) {

    //drawArcFill
    //Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
    endAngle = startAngle + (2 * Math.PI * (Math.abs(myVal) / myTotal));

    ctx.beginPath();
    ctx.moveTo(100, 100);
    ctx.arc(100, 100, (canvas.height / 8), startAngle, endAngle, false);
    ctx.lineWidth = 2.5;
    ctx.closePath();
    ctx.strokeStyle = myCol;
    ctx.stroke();

    startAngle = endAngle;
}

//write static texts
var timer = 100;
function animate(startTime) {

    // update
    var time = (new Date()).getTime() - startTime;
    var linearSpeed = timer;//increase to run faster, decrease to slow down
    // pixels / second
    var newX = linearSpeed * time / (5 * timer);//keep the 1000, it is per second
    var myVal = 0;
    var myName = '';
    var i = 0;

    //draw cycles on the same axis
    for (i = 0; i < data.length; ++i) {
        myCycle.x = 50;//cycles on the same axis
        // Extract the data
        var values = data[i].split(",");
        myName = values[0];
        myVal = values[1];
    
    }

    if (newX <= parseInt(myVal)) {
            drawArc(myVal, myName)
        };
   
    // request new frame
    requestAnimFrame(function () {
        animate(startTime);
    });

}
// wait one second before starting animation
setTimeout(function () {
    var startTime = (new Date()).getTime();
    animate(startTime);
}, timer);
<canvas id="myCanvas" width="800" height="900"></canvas>


Solution

  • There are 2 changes you need to make

    1. Remove the ctx.moveTo(400, 450); and ctx.closePath();. These are causing the lines to be drawn

    2. Convert the angles to radians before passing into you canvas drawing functions.

    Here's the final code

    <!DOCTYPE HTML>
    <html>
    
    <head>
      <title>ColumnCycle Growth</title>
      <style>
        body {
          margin: 0px;
          padding: 0px;
        }
      </style>
    </head>
    
    <body>
      <canvas id="myCanvas" width="800" height="900"></canvas>
      <script>
        window.requestAnimFrame = (function(callback) {
          return window.requestAnimationFrame || window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame || window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function(callback) {
              window.setTimeout(callback, 500 / 60);
            };
        })();
    
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        var data = ['Red,250', 'Blue,530', 'Orange,390', 'Green,190', 'Purple,450', 'Brown,600'];
    
        var myCycle = {
          x: canvas.width / 2,
          y: (canvas.height / 2) - 200, //add to push down
          radius: 10,
          colour: '#8ED6FF'
        };
    
        //draw the object first, then animate it
        var myTotal = 1810;
        var lastend = -Math.PI / 2;
        var currentEndAngle = 0;
        var currentStartAngle = 0;
        var radius = (canvas.height) / 6;
    
        var startAngle = -1 * Math.PI / 2;
        var endAngle = startAngle + (Math.abs(190) / myTotal);
    
    
        function drawArc(myVal, myCol) {
    
          //drawArcFill
          //Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
          endAngle = startAngle + (2 * Math.PI * (Math.abs(myVal * Math.PI / 180) / myTotal));
    
          ctx.beginPath();
          // ctx.moveTo(400, 450);
          ctx.arc(400, 450, (canvas.height / 4), startAngle, endAngle, false);
          ctx.lineWidth = 2.5;
          // ctx.closePath();
          ctx.strokeStyle = myCol;
          ctx.stroke();
    
          startAngle = endAngle;
        }
    
        //write static texts
        var timer = 100;
    
        function animate(startTime) {
    
          // update
          var time = (new Date()).getTime() - startTime;
          var linearSpeed = timer; //increase to run faster, decrease to slow down
          // pixels / second
          var newX = linearSpeed * time / (5 * timer); //keep the 1000, it is per second
          var myVal = 0;
          var myName = '';
          var i = 0;
    
          //draw cycles on the same axis
          for (i = 0; i < data.length; ++i) {
            myCycle.x = 50; //cycles on the same axis
            // Extract the data
            var values = data[i].split(",");
            myName = values[0];
            myVal = values[1];
    
          }
    
          if (newX <= parseInt(myVal)) {
            drawArc(myVal, myName)
          };
    
          // request new frame
          requestAnimFrame(function() {
            animate(startTime);
          });
    
        }
        // wait one second before starting animation
        setTimeout(function() {
          var startTime = (new Date()).getTime();
          animate(startTime);
        }, timer);
      </script>
    
    </body>
    
    </html>