views:

89

answers:

4

I'm trying to create a basic strobe light in the browser using the canvas element. I'm expecting setInterval to keep calling the changeBG function to change to a random background color. This function works fine on its own, but not when called by setInterval. I tried pulling up this page in firebug and it told me that colors was undefined. Here's the problematic code.

<html>
<head>
    <title>Strobe!</title>
    <link rel="stylesheet" type="text/css" href="reset.css" />
    <script type="text/javascript">
        function changeBG(colors,ctx,canvas) {                
            ctx.fillStyle = colors[Math.floor(Math.random()*colors.length)]
            ctx.fillRect(0,0,canvas.width,canvas.height)
        }

        function eventLoop() {
            var colors = ['#000000','#ff0000','#00ff00','#0000ff','#ffff00','#ff00ff','#00ffff']
            var canvas = document.getElementById('mainCanvas')
            var ctx = canvas.getContext('2d')
            canvas.width = window.innerWidth
            canvas.height = window.innerHeight
            //changeBG(colors,ctx,canvas)
            setInterval("changeBG(colors,ctx,canvas)", 1000);               
        }
    </script>
</head>
<body onload="eventLoop()">
    <canvas id="mainCanvas" width="800" height="600">
    </canvas>
</body>

I'm new to javascript so any insight what so ever would be highly appreciated.

A: 

try this UPDATED setInterval(function(){changeBG(colors,ctx,canvas)}, 1000);

shox
+1 looks good to me
Matt Williamson
I do not think you want arguments declared in the anonymous function in this example. That will prevent the existing variables from being used because the function will expect them as inputs and treat them as undefined if they're not provided.
g.d.d.c
yes we dont need to redeclare it ...
shox
A: 

You can pass directly a function, instead of a string to evaluate, as

setInterval(function(){changeBG(colors,ctx,canvas)}, 1000);

Good luck provoking epilepsy to someone

Chubas
A: 

The root problem is variable scope when the interval code is executed, colors and the other variables are not in scope.

Try this:

<html>
<head>
<title>Strobe!</title>
<link rel="stylesheet" type="text/css" href="reset.css" />
<script type="text/javascript">           
    function eventLoop() {
        var colors = ['#000000','#ff0000','#00ff00','#0000ff','#ffff00','#ff00ff','#00ffff']
        var canvas = document.getElementById('mainCanvas')
        var ctx = canvas.getContext('2d')
        canvas.width = window.innerWidth
        canvas.height = window.innerHeight            
        setInterval(function() {                
            ctx.fillStyle = colors[Math.floor(Math.random()*colors.length)]
            ctx.fillRect(0,0,canvas.width,canvas.height)
        }, 1000);               
    }    
</script>
</head>
<body onload="eventLoop()">
    <canvas id="mainCanvas" width="800" height="600">
    </canvas>
</body>
Allain Lalonde
+5  A: 

You code would work if you weren't passing a string to setInterval. Because it is in a string, it can't create a closure on the variables you are trying to use.

Try this instead:

setInterval(function() {
    changeBG(colors,ctx,canvas);
}, 1000)​;​

Using this method, you are passing an anonymous function to setInterval. It will call this function once per interval, which is 1000 miliseconds in this example.

The function can use the colors, ctx, and canvas variables because they exist in the scope where the function is declared. This creates a closure so that those variables still exist (as far as our anonymous function is concerned) when it is called over and over again.

For now, you can probably just use this code. For further understanding, I suggest researching anonymous functions and closures.

EndangeredMassa
Just for sake of completeness to the OP: If you declare your `colors`, `ctx` and `canvas` variables as global, without the `var`, the code works. Why? Because the vars are now global. And god kills a kitten (almost) every time someone does so.
Chubas
Much appreciated. I had tried the version with dead kittens, but my conscience couldn't take it.
Colin Barnes
@Chubas: Correct. I was going to mention that, but forgot!
EndangeredMassa