1

The code (pure js) is for creating animated rainbow in which the successive rainbows are delayed by a bit of time. But the animation is not consistent (slows down eventually). I am just a beginner in programming so my code is getting lengthy too.

if you run the code on local, the animation slows down after a bit of time and there is no consistency in the way the rainbows are coming out (there should be a time gap between each rainbow). My second issue is I want to reduce the code so that I dont have to create a function for each and every rainbow animation.

function anim()
{
  var x,y,z,p,q,r;
  x = y = z = p = q = r  = 2*Math.PI;
  var c = document.getElementById("myCanvas");
  var ctx = c.getContext("2d");
  ctx.clearRect(0,0,c.width,c.height);

  var id = setInterval(frame_one,1);
  var t = setInterval(frame_two,1);
  var u = setInterval(frame_three,1);
  var v = setInterval(frame_four,1);
  var w = setInterval(frame_five,1);
  var s = setInterval(frame_six,1);
  function frame_one()
  {
    if (x <=(Math.PI)) 
    {
      clearInterval(id);
      x = 2*Math.PI;
    }
    else
    {
      x = x - 0.036;
      ctx.lineWidth = 20;   
      ctx.beginPath();
      ctx.arc(c.width/2, c.height/2, c.height/2-20, 2* Math.PI,x,true);
      ctx.strokeStyle="red";
      ctx.stroke();   
    }  
  }  
  function frame_two()
  {
    if (y <= (Math.PI)) 
    {
      y  = 2*Math.PI;
      clearInterval(t);
    }
    else 
    {
      y= y - 0.032;   
      ctx.beginPath();
      ctx.lineWidth=20;
      ctx.arc(c.width/2,c.height/2, c.height/2-40, 2* Math.PI,y,true);
      ctx.strokeStyle="orange";
      ctx.stroke();  
    }
  }  
  function frame_three()
  {
    if (z <= (Math.PI)) 
    {
      clearInterval(u);
    }
  else 
    {
      z = z - 0.028;   
      ctx.beginPath();
      ctx.lineWidth = 20;
      ctx.arc(c.width/2,c.height/2,(c.height)/2-60, 2* Math.PI,z,true);
      ctx.strokeStyle = "yellow";
      ctx.stroke();
    }
  }  
  function frame_four()
  {
    if (p <= (Math.PI)) 
    {
        clearInterval(v);
    }
    else 
    {
      p = p - 0.024;   
      ctx.beginPath();
      ctx.lineWidth = 20;
      ctx.arc(c.width/2,c.height/2,(c.height)/2-80, 2* Math.PI,p,true);
      ctx.strokeStyle = "green";
      ctx.stroke();
    }
  }  
  function frame_five()
  {
    if (q <= (Math.PI)) 
    {
      clearInterval(w);
    }
    else 
    {
      q = q - 0.020;   
      ctx.beginPath();
      ctx.lineWidth = 20;
      ctx.arc(c.width/2,c.height/2,(c.height)/2-100, 2* Math.PI,q,true);
      ctx.strokeStyle = "blue";
      ctx.stroke();
    }
  }  
  function frame_six()
  {
    if (r <= (Math.PI)) 
    {
      clearInterval(s);
    }
    else 
    {
      r = r - 0.016;   
      ctx.beginPath();
      ctx.lineWidth = 20;
      ctx.arc(c.width/2,c.height/2,(c.height)/2-120, 2* Math.PI,r,true);
      ctx.strokeStyle = "violet";
      ctx.stroke();
    }
  }         
}
anim();
setInterval(anim,3000);
<canvas onclick="info()" id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;"></canvas>
TGrif
  • 5,725
  • 9
  • 31
  • 52
John Mayer
  • 43
  • 4
  • 1
    I'm not quite sure what you problem is, would you mind elaborating? – Yoshi Aug 18 '17 at 09:23
  • if u run the code on local, the animation slows down after a bit of time and there is no consistency in the way the rainbows are coming out(there should be a time gap between each rainbow). My second issue is i want to reduce the code so that i dont have to a function for each and every rainbow animation – John Mayer Aug 18 '17 at 10:08

1 Answers1

1

This is just one of several approaches. One of the reason why the inner circle seems to animate slower is due to its size: since it's smaller it will move in smaller step in the same period of time. You can compensate for this by reducing its duration time.

Example code

Using stroke combined with line-width instead of fills allows us to use cap types such as "round" (shown below), but it also simplifies the calculations needed.

And of course, I would recommend clearing each frame to remove overlapping anti-aliased pixels which will make a hard edge.

var ctx = c.getContext("2d"),
   /* All these settings are dynamic so one can alter number of colors,
      sizes, line width etc. without modifying the code */
    colors = ["red", "orange", "yellow", "green", "blue", "violet"],
    radius = 140,    // max radius
    lineWidth = 16,  // width of each arc in pixels
    delay = 300,     // ms
    duration = 1000, // ms (per arc)
    startTime;       // for animation loop

// initialize common line width and cap
ctx.lineWidth = lineWidth;
ctx.lineCap = "round";

// helper: draw arc from start to end angle at given color and position
// arc(): https://devdocs.io/dom/canvasrenderingcontext2d/arc
function arc(radius, angle, color) {
  ctx.beginPath();  // clear existing path and sub-paths
  ctx.arc(c.width*0.5, c.height, radius, angle, Math.PI*2); // end-angle always 360°
  ctx.strokeStyle = color;
  ctx.stroke();     // render arc
}

function draw(time) {
  if (!startTime) startTime = time;     // initialize start time if none is initialized
  ctx.clearRect(0,0,c.width,c.height);  // clear canvas per frame

  // iterate over color-array, then for each color entry:
  colors.forEach(function(color, i) {
  
    /* Calc t to a normalized value. We're interested in the values primarily
       between [0, 1]. To offset the delay we can use the current index times
       delay. We subtract it from current time so get a delay.
       startTime is the subtracted from this so we are relative to the beginning
       of the animation.
       And finally divide on duration to normalize.
    */
    var t = ((time - i * delay) - startTime) / duration;
    
    /* t may be lower than 0; we're only interested in t when it is equal or
       more than 0. We don't care about it being above 1 since we will clamp
       the angle below and we need to redraw each arc per frame */
    if (t >= 0) {
    
      /* arc(radius, startAngle, color) 
         Here we calculate radius from max minus linewidth times index of color.
         For start angle we start at 360° (in radians which is 2xPI).
         Then we use normalized t to get a value of 180° (or PI in radians).
         We subtract this from 360 so we go from [360°, 180°], e.g. drawing
         an arc from right side to left going in an arc over the canvas (not under).
         And finally we pass in the current color
      */
      arc(radius - lineWidth * i, Math.max(Math.PI, Math.PI * 2 - Math.PI * t), color);
    }
  });
  
  /* Animate until all are drawn. We calculate max time using duration + each delay
     times the number of colors */
  if (time < startTime + colors.length * delay + duration) requestAnimationFrame(draw);
}

// invoke animation passing a time argument
requestAnimationFrame(draw);
<canvas id=c></canvas>
Community
  • 1
  • 1