I have a page which has several <canvas> elements.
I am passing the canvas ID and an array of data to a function which then grabs the canvas info and passes the data onto a draw() function which in turn processes the given data and draws the results onto the canvas. So far, so good.
Example data arrays;
$(function() {
setup($("#canvas-1"), [[110,110,100],
[180,180,50],
[220,280,80]]);
setup($("#canvas-2"), [[110,110,100],
[180,180,50],
[220,280,80]]);
});
setup function;
function setup(canvas, data) {
ctx = canvas[0].getContext('2d');
var i = data.length;
var dimensions = {
w : canvas.innerWidth(),
h : canvas.innerHeight()
};
draw(dimensions, data, i);
}
This works perfectly. draw() runs and each canvas is populated.
However - I need to animate the canvas. As soon as I replace line 8 of the above example;
draw(dimensions, data, i);
with
setInterval( function() { draw(dimensions, data, i); }, 33 );
It stops working and only draws the last canvas (with the others remaining blank).
I'm new to both javascript and canvas so sorry if this is an obvious one, still feeling my way around. Guidance in the right direction much appreciated! Thanks.
The problem has to do with how closures work. Closures don't receive a copy of the data, they receive an active reference to it. So when the function is run later, it references a live copy of i and such -- which have moved on since you set up the call.
You can fix it like this:
drawLater(dimensions, data, i);
...with this defined elsewhere:
function drawLater(dimensions, data, i) {
setInterval(function() { draw(dimensions, data, i); }, 33 );
}
That works because the closure holds a reference to the arguments to drawLater, rather than to the variables in your loop.
Separately: Shouldn't you be passing the canvas or its ID into that somewhere?
How does draw() know which canvas to use? My guess from your code is that you're using the global variable ctx which will get overwritten with every call to setup(). So you need to change draw() and add the canvas to use as the first parameter.
JavaScript variables are function scoped, not block scoped. Also you need to declare ctx with var to make it local and then pass it to the draw function.
I think you should be able to make it work if you try the following :
setInterval( 'draw(dimensions, data, i);', 33 );
Hope this helps :)
Related
i am making a game based on draw images and clear it every some part of second.
i started with:
var peng = new Image();
and then:
peng.onload = function() {
context.drawImage(peng, pengXPosition, pengYPosition, pengWidth, pengHight);
};
and in the loop:
var i=0;
function pengMoveRight(){ i++;if(i==1){peng.src = 'images/1.png';}else if(i==2)
{peng.src = 'images/2.png';} else if(i==3){peng.src = 'images/3.png';}else if(i==4){
peng.src = 'images/4.png';}else if(i==5){peng.src = 'images/5.png';}else if(i==6){
peng.src = 'images/6.png';i-=6;}}
when i run it it works well on IE but on chrome and mozilla it`s too slow and the character is about to disappear .. i used setinterval(); once and window.requestAnimationFrame(); once and both of them cause the same problem.
what should i do to make it smooth move?
here is the full script http://mark-tec.com/custom%20game/
Instead of changing the source, try to create several Image objects instead. That way, the drawImage call can always use a pre-loaded image.
You need to preload all the images or use the sprite method (all images packed into a single sprite) in order to avoid the initial delay caused by the image loading only when it's needed.
However, after that initial problem, your example should run fine once all the images are cached.
Since downloading the current version of chrome (Version 31.0.1650.57) I've been completely unable to draw images in a HTML5 Canvas with my code; there are no errors and it's finding the resources, they just aren't drawing. I'd really appreciate some help on this!
var grass_img = new Image();
grass_img.src = 'grass.gif';
grass_img.onload = draw_here(grass_img, (center_x + base_x + xpos), (center_y + base_y + ypos),1);
Which appears here and there on several different images and calls:
function draw_here(image, x, y, scale){
draw_canv.drawImage(image, x, y, image.width * scale, image.height * scale);
}
X and Y are correct, as is the scale; there are no coding errors picked up by the debugger and the program worked perfectly until the latest version of chrome came out. Downgrading chrome is also not an option.
The problem is in this line:
grass_img.onload = draw_here( /* ... */ );
You seem to think that you assign the function draw_here as an onload-handler. But that's not what really happens in this line. What you really do is execute draw_here immediately and assign the return-value of that function (which is undefined) to grass_img.onload.
Try this instead:
grass_img.onload = function() {
draw_here(grass_img, (center_x + base_x + xpos), (center_y + base_y + ypos),1);
}
This creates an anonymous function which is assigned to the onload-handler. When that anonymous function is called (which will happen when the load-event is triggered) it calls your draw-handler with your arguments.
I solved the problem. It seems that the latest version of Chrome does not like it if you declare your image in every frame iteration. (Something it seems to have allowed previously, but I really shouldn't have been doing.) If you declare your images and their sources at the top of your js, outside of any functions, and only draw them in functions then the problem is solved, eg:
var image = new Image();
image.src='image.jpg';
someFunction() {
canvas.drawImage(image,0,0);
}
Note: .src should always be called AFTER .onload. Otherwise lots of hard-to-track-down buggies (like this one) appear. Especially if your image loads before onload can be parsed. Your currently accepted solution is possibly functional due to a race condition with function overhead timing.
I am trying to use RGraph library to manipulate a vertical progress bar, but I'm stuck because after creating the progress bar I can't manipulate its properties, here's the code I have been using:
window.onload = function ()
{
// Create the object. The arguments are: The canvas ID, the indicated value and the maximum value.
var myProgress = new RGraph.VProgress('myProgress', 60, 100)
// Configure the chart to look as you want.
.Set('chart.colors', ['blue'])
.Set('chart.tickmarks',['true'])
.Set('chart.tickmarks.color',['white'])
.Set('chart.title',"Nivel")
// Now call the .Draw() method to draw the chart.
.Draw();
}
and after that I try to read the value property using a click on a button,but it says undefined:
function myFunction()
{
var valor= myProgress.value;
alert(valor);
}
How can I access the properties of an object created in another function? My idea is to change the level of the progress bar according to a control put in the webpage.
Thanks in advance for any help supplied.
myProgress is scoped within your onload handler, thus it can't be seen by other functions out of that scope.
var myProgress;
window.onload = function() {
myProgress = new RGraph.VProgress('myProgress', 60, 100)
// ...
};
I'm using some javascript to allow users to dynamically load a sketch on click to a canvas element using:
Processing.loadSketchFromSources('canvas_id', ['sketch.pde']);
If I call Processing.loadSketchFromSources(...) a second (or third...) time, it loads a second (or third...) .pde file onto the canvas, which is what I would expect.
I'd like for the user to be able to click another link to load a different sketch, effectively unloading the previous one. Is there a method I can call (or a technique I can use) to check if Processing has another sketch running, and if so, tell it to unload it first?
Is there some sort of Processing.unloadSketch() method I'm overlooking? I could simply drop the canvas DOM object and recreate it, but that (1) seems like using a hammer when I need a needle, and (2) it results in a screen-flicker that I'd like to avoid.
I'm no JS expert, but I've done my best to look through the processing.js source to see what other functions may exist, but I'm hitting a wall. I thought perhaps I could look at Processing.Sketches.length to see if something is loaded already, but simply pop'ing it off the array doesn't seem to work (didn't think it would).
I'm using ProcessingJS 1.3.6.
In case someone else comes looking for the solution, here's what I did that worked. Note that this was placed inside a closure (not included here for brevity) -- hence the this.launch = function(), blah blah blah... YMMV.
/**
* Launches a specific sketch. Assumes files are stored in
* the ./sketches subdirectory, and your canvas is named g_sketch_canvas
* #param {String} item The name of the file (no extension)
* #param {Array} sketchlist Array of sketches to choose from
* #returns true
* #type Boolean
*/
this.launch = function (item, sketchlist) {
var cvs = document.getElementById('g_sketch_canvas'),
ctx = cvs.getContext('2d');
if ($.inArray(item, sketchlist) !== -1) {
// Unload the Processing script
if (Processing.instances.length > 0) {
// There should only be one, so no need to loop
Processing.instances[0].exit();
// If you may have more than one, then use this loop:
for (i=0; i < Processing.instances.length; (i++)) {
// Processing.instances[i].exit();
//}
}
// Clear the context
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, cvs.width, cvs.height);
// Now, load the new Processing script
Processing.loadSketchFromSources(cvs, ['sketches/' + item + '.pde']);
}
return true;
};
I'm not familiar with Processing.js, but the example code from the site has this:
var canvas = document.getElementById("canvas1");
// attaching the sketchProc function to the canvas
var p = new Processing(canvas, sketchProc);
// p.exit(); to detach it
So in your case, you'll want to keep a handle to the first instance when you create it:
var p1 = Processing.loadSketchFromSources('canvas_id', ['sketch.pde']);
When you're ready to "unload" and load a new sketch, I'm guessing (but don't know) that you'll need to clear the canvas yourself:
p1.exit();
var canvas = document.getElementById('canvas_id');
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
// Or context.fillRect(...) with white, or whatever clearing it means to you
Then, from the sound of things, you're free to attach another sketch:
var p2 = Processing.loadSketchFromSources('canvas_id', ['sketch2.pde']);
Again, I'm not actually familiar with that library, but this appears straightforward from the documentation.
As of processing.js 1.4.8, Andrew's accepted answer (and the other answers I've found in here) do not seem to work anymore.
This is what worked for me:
var pjs = Processing.getInstanceById('pjs');
if (typeof pjs !== "undefined") {
pjs.exit();
}
var canvas = document.getElementById('pjs')
new Processing(canvas, scriptText);
where pjs is the id of the canvas element where the scrips is being run.
For my first foray into html5, I'm trying to make a scoreboard for the game Farkle.
I have figured out how to make a lame looking but functional graph system for up to five players, but I cannot figure out how to have it update without simply drawing over the old bar graphs.
I realize that my code could be much better, but I didn't want to write helper functions until I was sure how it would work.
I'm using buttons to trigger the "score"function for the appropriate player for now,
but I would like to be more automatic.
Here is a "working" version, so that you can see my problem.
http://jsfiddle.net/kaninepete/dnsuj/1/
For graphical stuff, I organize my code into a setup() function, and a draw() function that gets called repeatedly, something like this:
function setup(){
// set up variables you in draw, here
// examples include initial document width and height, and things
// like that (although those in particular would go in draw if you
// want to handle the resize event.
context = document.getElementById("canvas").getContext('2d');
}
function draw(){
// clear the canvas
context.clearRect(0, 0, docWidth, docHeight);
// now redraw your bar graph
// ...
}
setup();
setInterval("draw();", 60);
Do you see where I'm going with this?
the basic of the periodic update:
function update() {
score1();
//you can add here everything you can write here loops, you can write here functions;
move_to(100,100);
alert('hello');
for(i=0;i<100;i++) {
j=2; //this is not needed, just to demonstrate you can do everything here
}
//this is the important, that call himself periodically again;
window.setTimeout(100,update);
}
update();
this code calls an update function wich periodically calls himself. this is the best way to update something like canvas.