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.
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.
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
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>
try this
UPDATED
setInterval(function(){changeBG(colors,ctx,canvas)}, 1000);
Related
I have been trying this simple animation with html 5 and javascript from here:
http://www.developphp.com/view.php?tid=1262
The code is follwoing:
<!-- Lesson by Adam Khoury # www.developphp.com -->
<!-- Watch the video to get code explanation line by line -->
<!-- http://www.youtube.com/watch?v=hUCT4b4wa-8 -->
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
canvas{border:#666 1px solid;}
</style>
<script type="text/javascript">
function draw(x,y){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.save();
ctx.clearRect(0,0,550,400);
ctx.fillStyle = "rgba(0,200,0,1)";
ctx.fillRect (x, y, 50, 50);
ctx.restore();
x += 1;
var loopTimer = setTimeout('draw('+x+','+y+')',100);
}
</script>
</head>
<body>
<button onclick="draw(0,0)">Draw</button>
<canvas id="canvas" width="550" height="400"></canvas>
</body>
</html>
There is just one thing i don't understand in the code: in the setTimeout method what does the '+' before and after x and y do and why quotes are used to enclose +x+ and +y+?
'draw('+x+','+y+')' This is a just string concatination you will understand this by printing that string. following fiddle explains more.
http://jsfiddle.net/zebqqcee/
The setTimeout will execute the instruction with an eval, I mean this will execute when the timeout pop this:
eval('draw('+x+','+y+')');
If you don't like this syntax, I personally don't, you can use this:
var loopTimer = setTimeout(function() {
draw(x,y);
},100);
I am using EaselJS and my task is to move a .png from right to left.
Since I bumped into an error I can't fix, I copied a work with the same goal and using createjs.Ticker.addListener to keep everything updated. I opened the other index.html with the included javascript and it worked perfectly. I proceeded to use this code as an example, but used my graphics. It is almost the same code, but it still tells me "TypeError: createjs.Ticker.addListener is not a function" I have no idea why the example works fine, but my code screws up like that.
<html>
<head>
<title>Title</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="js/easeljs.js"></script>
<script>
var canvas, stage, child;
function draw () {
canvas = document.getElementById("canvas");
stage = new createjs.Stage( canvas );
var bg = new createjs.Bitmap( "bg.png" );
stage.addChild( bg );
var child = new createjs.Bitmap( "target.png" );
child.onTick = function ()
{
this.x --;
}
stage.addChild( child );
child.y = 100;
child.x = 100;
createjs.Ticker.useRAF = true;
createjs.Ticker.setFPS( 1 );
createjs.Ticker.addListener( stage , true );
createjs.Ticker.addListener( window , true );
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas" width="650" height="400" style="background: #ccc;"></canvas>
</body>
</html>
EDIT
The error has been found. It wasn't the code, it was the easelJS-library itself that made the complications. I used a different version of the library and it worked. Thank you anyways :).
The function you are after is addEventListener
like this:
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick() {
stage.update();
}
CreateJS Ticker Documentation
<!doctype html>
<html>
<head>
<title>Canvas test</title>
</head>
<body>
<script type="text/javascript">
c = getElementById('canvas');
ctx = c.getContext("2d")'
ctx.fillRect(10,10,10,10);
</script>
<canvas id="canvas" height ="100" width = "100"></canvas>
</body>
</html>
I have tried this on Chrome and IE 9, and neither of them display anything. Do you know why doesn't this work?
It's probably something stupid, and my friend is asking me to post this as he's too lazy to sign up, but I couldn't figure it out myself.
You need to put your code in a onload function.
For example:
window.onload = function() {
c = document.getElementById('canvas');
ctx = c.getContext("2d");
ctx.fillRect(10,10,10,10);
};
The reason for this is because you javascrpt code will be running before the <canvas> renders, and you want it to run afterwords.
Demo: http://jsfiddle.net/maniator/nCf7Y/
Syntax error here:
ctx = c.getContext("2d")'
Change to:
ctx = c.getContext("2d");
Also, you need to use the document object:
c = document.getElementById('canvas');
You're missing 'document'.
var c = document.getElementById('canvas');
The canvas is not there when the script is running. Move the <canvas> tag so that it is before the <script> tag.
The code below seems to leak memory at a rather alarming rate on webkit (mobile safari & konqueror). I realize the test case could be rewritten to reuse the canvas instead of creating a new one, but I'm wondering why the below doesn't also work. Any insight would be appreciated.
<html>
<head>
<script>
function draw() {
var holder = document.getElementById("holder");
holder.innerHTML = "<canvas id=cnv height=250 width=250>";
var ctx = document.getElementById("cnv").getContext("2d");
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(Math.random()*100,Math.random()*100);
ctx.stroke();
}
function start() {
setInterval(draw, 100);
}
</script>
</head>
<body onload="start()">
<div id="holder"></div>
</body>
</html>
This issue happens on Webkit even when an image SRC is modified, so I don't get surprised if this happens when handling canvas too.
There is a bug filled on Chrome (that made them filling a bug to Webkit) and we hope this will be fixed soon, as it is making many Chrome extension unusable.
references
http://code.google.com/p/chromium/issues/detail?id=36142
https://bugs.webkit.org/show_bug.cgi?id=23372
Anyway, the suggestions above should mitigate this.
Don't:
Create the <canvas> element with .innerHTML
Create the <canvas> in each Interval
Do:
use var cv = document.createElement('canvas'); cv.setAttribute('height', '250'); // ...
cache the reference of cv at some init point and re-use that!!
<script>
var holder = document.getElementById("holder"),
var cv = document.createElement('canvas');
cv.setAttribute('id', 'cnv');
cv.setAttribute('height', '250');
cv.setAttribute('width', '250');
holder.appendChild(cv);
function draw() {
var ctx = cv.getContext("2d");
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(Math.random()*100,Math.random()*100);
ctx.stroke();
}
function start() {
setInterval(draw, 100);
}
</script>
I can't figure out why this isn't working. I draw an image onto a canvas, use getImageData, manipulate the pixels and then store that array of pixels for later. When I'm ready to draw those pixels later, I use createImageData on another, identically sized canvas, set the resulting ImageData object's data property to the array of pixels I saved and then call putImageData. The result: the saved array data isnt assigned to the ImageData object. Below is the code for a test page:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
<head>
<meta http-equiv='content-type' content='text/html;charset=UTF-8' />
<link rel="stylesheet" type="text/css" href="styles.css" media="screen" />
<script type='text/javascript' src='http://code.jquery.com/jquery-1.4.2.min.js'></script>
<title>Audio Browser</title>
</head>
<body>
<script type='text/javascript'>
$(document).ready(function()
{
var copy = document.createElement('canvas');
var viewer = $('#viewer')[0];
var output = $('#output')[0];
var ctx = viewer.getContext('2d');
var outputContext = output.getContext('2d');
var img = $('#input')[0];
var imageData;
var songs = [];
output.width = copy.width = img.width;
output.height = copy.height = img.height;
copy.getContext('2d').drawImage(img, 0, 0);
viewer.width = 1500;
viewer.height = 800;
originalData = copy.getContext('2d').getImageData(0, 0, copy.width, copy.height);
imageData = originalData.data;
for(var i = 3; i < imageData.length; i+=4)
{
var row = Math.floor(i / copy.width / 4);
imageData[i] *= 1-((copy.height - row)/copy.height*2);
}
songs.push({'fadeImg' : imageData});
draw();
function draw()
{
originalData = copy.getContext('2d').createImageData(copy.width, copy.height);
originalData.data = songs[0].fadeImg;
outputContext.putImageData(originalData, 0, 0);
}
});
</script>
<img id='input' src='albumArt/small.png' />
<canvas id='output'></canvas>
<br />
<canvas id='viewer'></canvas>
</body>
</html>
EDIT:
I just found something interesting when trying this in the new IE9 beta. In IE, I get the following error: SCRIPT65535: Invalid set operation on read-only property on originalData.data = songs[0].fadeImg; I checked firefox before and there are no errors in the error console. Maybe firefox just silently fails. If thats the case, how can I copy the array back without an extremely wasteful for loop?
I ran into this problem recently as well.
Unfortunately, it seems that straight assignment of the data is impossible because it is an Uint8ClampedArray.
According to Kronos.com :
It behaves identically to the other typed array views, except that the setters and constructor use clamping [WEBIDL] rather than modulo arithmetic when converting incoming number values.
So a loop is the way to go...
This should do:
for (var i in songs[0].fadeImg){
originalData.data[i] = songs[0].fadeImg[i];
}
It is very inconvenient, but this is the only way that I have found.