Wiw many answers. Here is an alternative.
Don't use setInterval. Use setTimeout. I assume you want the lights to change a preset time after each new light. setInterval should be avoided unless you can find no other way
The best way to do this is to create an object of light states. Each state leads to the next. The structure has the time the light is on and what the next state should be.
lights = {
red:{
timeOn:1000, // how long it is on for
display:["red","white","white"], // the colours
next:"amber", // what comes next.
},
amber:{
timeOn:2000,
display:["red","#F90","white"],
next:"green",
},
green:{
timeOn:3000,
display:["white","white","green"],
next:"amberGreen",
},
amberGreen:{
timeOn:1000,
display:["red","#F90","white"],
next:"red",
},
}
Add the draw functions,
var circle = function (x, y,col) {
ctx.beginPath();
ctx.fillStyle = col;
ctx.arc(x, y, 40, 0, Math.PI * 2);
ctx.stroke();
ctx.fill();
};
function draw(){
ctx.strokeRect(0, 0, 100, 240 + 4 * 20);
var posY = 50;
var lightInd = 0;
circle(50, posY, nextLightState.display[lightInd ++]);
circle(50, posY += 100, nextLightState.display[lightInd ++]);
circle(50, posY += 100, nextLightState.display[lightInd ++]);
// get the current time
var currentTime = new Date().valueOf();
// calc when the next light should be on
nextTime += nextLightState.timeOn;
// set the time out to that time. Sometimes the system can get hung up
// doing other stuff. You just have to make sure you have not gone over
// time and give setTimeout a negative value. If it does get delayed it
// will automatically switch states quickly and catch up to where it should
// be
setTimeout(draw, Math.max(0,nextTime-currentTime));
// set the next light state provided in the light state description
nextLightState = lights[nextLightState.next];
}
You want it to be very precise or you would not have used setInterval. As setTimeout can drift, you need to add just a little time keeping to ensure your timing stays correct.
Set up the canvas context state.
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
Now just need the current time and what the next light state will be. Then call the draw function and away it goes.
var nextTime = new date().valueOf(); // get the current time in ms
var nextLightState = lights.red; // set the next light state
draw(); // start it going.
Below is a working example.
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
// describe all the requiered states
var lights = {
red:{
timeOn:1000, // how long it is on for
display:["red","white","white"], // the light colours
next:"amber", // what the next state is
},
amber:{
timeOn:3000,
display:["white","#F90","white"],
next:"green",
},
green:{
timeOn:3000,
display:["white","white","#0F0"],
next:"amberGreen",
},
amberGreen:{
timeOn:1000,
display:["red","#F90","white"],
next:"red", // points to the first state
},
}
// draws a light
var circle = function (x, y,col) {
ctx.beginPath();
ctx.fillStyle = col;
ctx.arc(x, y, 40, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
};
ctx.lineWidth = 3;
ctx.strokeStyle = "black";
function draw(){
ctx.strokeRect(0, 0, 100, 240 + 4 * 20); // draw the box;
// set up the vars to draw lights
var posY = 50;
var lightInd = 0;
// no need for a loop as there are always 3 lights
circle(50, posY, nextLightState.display[lightInd ++]);
circle(50, posY += 100, nextLightState.display[lightInd ++]);
circle(50, posY += 100, nextLightState.display[lightInd ++]);
// get the current time
var currentTime = new Date().valueOf();
// workout when the next light state is due
nextTime += nextLightState.timeOn;
// set the timeout for the due time
setTimeout(draw,nextTime-currentTime)
// set the next light state from that requiered by the current state
nextLightState = lights[nextLightState.next];
}
var nextTime = new Date().valueOf(); // get the current time in ms
var nextLightState = lights.red; // set the next light state
draw(); // start it going.
#canV {
width:100px;
height:320px;
}
<canvas id="canV" width=100 height=320></canvas>
var myVar =. You can't declare the same variable multiple times in the same scope.