0

I am struggling to get my traffic lights to light up automatically and run continuously. The functions do not show all the lights, just the first and the last. I have tried using setInterval() but with no joy. It may be something to do with this line being in the wrong place. ctx.clearRect(0, 0, canvas.width, canvas.height); I am new to JavaScript and this is the first time I have used the canvas. Please could I get some help on this

This is my JSFiddle link: http://jsfiddle.net/nmrsjp/opv1cpyx/11/

Thank you in advance

var red = ["Red", "White", "White"]
var redAmber = ["Red", "Yellow", "White"]
var green = ["White", "White", "Green"]
var amber = ["White", "Yellow", "White"]

var x = 50
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");

var circle = function (x, y, radius) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, false);
    ctx.stroke();
    ctx.fill();
};
var draw = function (colour) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (i = 0; i < 3; i++) {
        ctx.lineWidth = 1;
        ctx.strokeStyle = "Black";
        ctx.fillStyle = colour[i];
        circle(50, x, 40);
        x = x + 120;
    }
    x = 50
}
var myVar = setInterval(function () {draw(red)}, 1000);
var myVar = setInterval(function () {draw(redAmber)}, 3000);
var myVar = setInterval(function () {draw(green)}, 3000);
var myVar = setInterval(function () {draw(amber)}, 3000);
<canvas id="canvas1" width="100" height="350" style="border:1px solid"></canvas>

3
  • Remove those 4 var myVar = . You can't declare the same variable multiple times in the same scope. Commented Nov 23, 2015 at 10:28
  • try to use setTimeout instead of setInterval. jsfiddle.net/opv1cpyx/12 Commented Nov 23, 2015 at 10:33
  • jsfiddle.net/opv1cpyx/15 Commented Nov 23, 2015 at 10:35

5 Answers 5

1

You're setting the color to red once every second. You're setting other colors every third second, they'll all be fired almost simultaneously, but will also be immediately overriden by setting red again.

I'm not sure about exactly how you want this to behave, but if you want a constant interval, but firing at different times, you need to delay the setting of the interval as well. Here's an example with 4s interval all delayed by 1s:

setInterval(function () { draw(red) }, 4000);
setTimeout(function() { setInterval(function () { draw(redAmber) }, 4000); }, 1000);
setTimeout(function() { setInterval(function () { draw(green) }, 4000); }, 2000);
setTimeout(function() { setInterval(function () { draw(amber) }, 4000); }, 3000);

Fiddle

It might be easier to define a sequence and then cycle through that, using just one interval:

var colourSequence = [red, redAmber, green, amber];
var position = -1;

function changeColour() {
    draw(colourSequence[++position % colourSequence.length]);
}

setInterval(changeColour, 1000);

Fiddle

Sign up to request clarification or add additional context in comments.

Comments

1

Your way for setting interval is not correct. Use like below:

http://jsfiddle.net/opv1cpyx/18/enter link description here

var red = ["Red", "White", "White"];
var redAmber = ["Red", "Yellow", "White"];
var green = ["White", "White", "Green"];
var amber = ["White", "Yellow", "White"];

var lights = [red, redAmber, green, amber];

var x = 50
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");

var circle = function (x, y, radius) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, false);
    ctx.stroke();
    ctx.fill();
};
var draw = function (colour) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0; i < 3; i++) {
    ctx.lineWidth = 1;
    ctx.strokeStyle = "Black";
    ctx.fillStyle = colour[i];
    circle(50, x, 40);
    x = x + 120;
}
x = 50;
}

var li=0;
var myVar = setInterval(function () {
    if(li > 3)
        li = 0;
    draw(lights[li++]);
}, 3000);

Comments

0

Try this:

Fiddle

var red = ["Red", "White", "White"]
var redAmber = ["Red", "Yellow", "White"]
var green = ["White", "White", "Green"]
var amber = ["White", "Yellow", "White"]

var x = 50
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");

var circle = function(x, y, radius) {
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI * 2, false);
  ctx.stroke();
  ctx.fill();
};
var draw = function(colour) {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (i = 0; i < 3; i++) {
    ctx.lineWidth = 1;
    ctx.strokeStyle = "Black";
    ctx.fillStyle = colour[i];
    circle(50, x, 40);
    x = x + 120;
  }
  x = 50
}
var index = 0;
var myVar = setInterval(function() {
  switch (index) {
    case 0:
      draw(red);
      break;
    case 1:
      draw(green);
      break;
    case 2:
      draw(amber);
      break;
  }
  index = (index + 1) % 3;
}, 1000);
<canvas id="canvas1" width="100" height="350" style="border:1px solid"></canvas>

Comments

0

Try This

  $(document).ready(function () {
    var red = ["Red", "White", "White"]
    var redAmber = ["Red", "Yellow", "White"]
    var green = ["White", "White", "Green"]
    var amber = ["White", "Yellow", "White"]

    var x = 50
    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext("2d");

    var circle = function (x, y, radius) {
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, Math.PI * 2, false);
        ctx.stroke();
        ctx.fill();
    };
    var draw = function (colour) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (i = 0; i < 3; i++) {
            ctx.lineWidth = 1;
            ctx.strokeStyle = "Black";
            ctx.fillStyle = colour[i];
            circle(50, x, 40);
            x = x + 120;
        }
        x = 50
    }
    setTimeout(function () { setInterval(function () { draw(red) }, 4000); }, 1000);
    setTimeout(function () { setInterval(function () { draw(redAmber) }, 4000); }, 2000);
    setTimeout(function () { setInterval(function () { draw(green) }, 4000); }, 3000);
    setTimeout(function () { setInterval(function () { draw(amber) }, 4000); }, 4000);
});

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.