I am writing code for a HTML5 Canvas animation using Javascript. I am using requestAnimFrame for that. Animation is working fine for a point. But when i add a loop(for or while) in the function which is using requestAnimFrame or setTimeout, animation doesnt work. Its important for me to add the loop. ANy Suggestions to make that possible?
function animate(lastTime) {
var date = new Date();
var time = date.getTime();
var timeDiff = time - lastTime;
var linearSpeed = 100;
var linearDistEachFrame = linearSpeed * timeDiff / 1000;
var currentX = LINE.x;
var currentY = LINE.y;
var newY = currentY + linearDistEachFrame;
var newX = currentX + linearDistEachFrame;
context.beginPath();
context.moveTo(LINE.x, LINE.y);
lastTime = time;
var Xindex=LINE.arrayX.indexOf(newX);
//here am getting error..if i replace this with 'if' its working fine..and even if there is not a single LOC it doesnt work
while(Xindex!=-1) {
//processes the loop
}
context.lineTo(LINE.x, LINE.y);
context.fillStyle = "red";
context.fill();
context.lineWidth = LINE.borderWidth;
context.strokeStyle = "red";
context.stroke();
// request new frame
requestAnimFrame(function() {
animate(lastTime);
});
}
Try adding a break statement in the loop and see if that fixes it. If it does, it means that the condition has been met, and the code will be stuck in the loop forever unless you break out, or change Xindex to -1.
You need to narrow down exactly where the code is failing. One way to do this is by printing out debug statements at key parts of the code, so you know for sure they were executed, and what the value of important variables was.
You can use console.log("test"); for example, to write to Chrome's JavaScript console, or Firebug, or similar.
One trouble you'll face with debug output for a working animation program is the screeds of output. You might want to log only in certain interesting circumstances or you'll be drowning in a stream. Of data.
Related
Working on a simple game, using delta time for the first time.
Delta time (dt) is created in this function:
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.00;
update(dt);
render();
lastTime = now;
requestAnimationFrame(main);
};
gameTime (my second variable) is just created as a var.
var gameTime = 0;
The problem comes when I try to add dt to gameTime, here:
function update(dt){
gameTime += dt;
};
this returns NaN, whatever I seem to do to it. I can display gameTime, and add to it using ++, and I can display dt (0.017 usually), but as soon as I add one to the other, I get NaN.
How can I fix this?
Does your lastTime contain anything before running main() for the first time?
If not, then this code var dt = (now - lastTime) / 1000.00; can't work because lastTime is undefined
First of all, if you return you can see your result which is not NaN, basically you write it like this :
function update(dt)
{
return gameTime += dt;
};
Second of all,if you are not using this code in vanilla javascript and it's related to unity, try to initiate your variables. like how you defined var gameTime = 0;, check other variables and initiate them.
I have built a script which takes a sequence of images and displays them on a canvas element in a animation loop.
This works really well on my desktop, but on IPAD (3 retina) it is very slow. Could you suggest any way to improve the performance?
var videoItts = 0;
function playVideo() {
if(videoItts < 92) {
setTimeout(function() {
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.drawImage(imagesL[videoItts],0,0,1024,636);
requestAnimationFrame(playVideo);
videoItts ++;
}, 1000/22)
}
}
requestAnimationFrame(playVideo);
The imagesL is an array of pre-loaded images.
I would suggest not mixing setTimeout and requestAnimationFrame. You can solve it using only requestAnimationFrame:
var startTime = Date.now();
var fps = 22;
var lastDrawnIndex = null;
var totalFrames = 92;
function drawVideo() {
var currTime = Date.now();
var currFrameIndex = Math.round((currTime - startTime) / (1000/fps)) % totalFrames;
// Since requestAnimationFrame usually fires at 60 fps,
// we only need to draw the image if the frame to draw
// actually has changed from last call to requestAnimationFrame
if (currFrameIndex !== lastDrawnIndex) {
ctx.drawImage(imagesL[videoItts],0,0,1024,636);
lastDrawnIndex = currFrameIndex;
}
requestAnimationFrame(drawVideo);
}
requestAnimationFrame(drawVideo);
The idea is that for every call to requestAnimationFrame we calculate, based on the elapsed time and the desired animation frame rate, which frame index to draw. If it's different from last calculated frame index we draw it. Then we schedule drawVideo to be called next animation frame by calling requestAnimationFrame(drawVideo) at the end.
The code above will loop frames 0-91 continously at 22 fps. I removed the ctx.clearRect call, it is only needed if the frames contains transparency. So you might want to add that back.
Allright, I know what machine precision is, but this, I can't understand...
Code:
console.log("meanX",meanX);
meanX2 = meanX * meanX; //squared
console.log("meanX2",meanX2);
Console output:
meanX 300.3
meanX2 28493.4400000000002
In case you are wondering, the correct value for meanX2 would be 90180.09
And this is only one of the many examples visible in the screenshot..
.toFixed(6) seems to fix this... But I have no idea why it doesn't work without it.
Edit
Ok, I don't want to post the whole program code here because in first place I'm not the only author, and second, I also wouldn't like this to be copied without our permission. But I'll gladly explain how I get this error and will post the whole method/function code here.
This code belongs, as you may have guessed from the window title, to a lane detection algorithm. We use Three.js/webgl to run some pre processing shaders on each frame of a video and then we analyze the resulting image. The method/function you see on the screenshot is a perpendicular line fitting algorithm and is part of the whole thing.
I can see the algorithm running nicely because I have the lane being drawn on top of the video, and It is well placed. Until suddenly the lane turns into an horizontal bar. This unexpected behavior happens exactly because of the phenomenon I described here, since it's from that moment that I start to see wrong math in the console.
Also, because the video and algorithm run at slightly different fps everytime, the problem doesn't always happen in the same moment of the video, and sometimes It doesn't happen at all.
Here is the code (it has some alterations because I was trying to isolate the issue):
this.perpendicularLineFit = function (points, slopeSign) {
var count = points.length;
var sumX = 0,
sumY = 0;
var sumX2 = 0,
sumY2 = 0,
sumXY = 0;
var meanX, meanY;
var i, lowp = {}, highp = {};
var B;
var slope;
var originY;
for (i = 0; i < count; i++) {
sumX += points[i].x;
sumY += points[i].y;
sumX2 += points[i].x * points[i].x;
sumY2 += points[i].y * points[i].y;
sumXY += points[i].y * points[i].x;
}
meanX = sumX / count;
meanY = sumY / count;
//If you uncoment this, problem reappears:
//var numeratorLeft = meanY * meanY;
console.log("meanX",meanX);
var meanX2 = meanX*meanX;
console.log("meanX2",meanX2);
var numerator = (sumY2 - count * (meanY * meanY)) - (sumX2 - count * meanX2);
var denominator = (count * meanX * meanY - sumXY);
B = 0.5 * (numerator / denominator);
slope = -B + slopeSign * Math.sqrt(B * B + 1);
originY = meanY - slope * meanX;
slope = isNaN(slope) ? slopeSign : slope;
originY = isNaN(originY) ? originY : originY;
lowp.y = this.lowY;
lowp.x = (this.lowY - originY) / slope;
highp.y = this.highY;
highp.x = (this.highY - originY) / slope;
return {
low: lowp,
high: highp
};
};
Now, I was trying to understand what was causing this, and the most bizarre thing is that it seems that when I place a statement of this form
var x = ... meanY * meanY ...;
before the meanX2 attribution, the issue happens. Otherwise it doesn't.
Also, I tried to catch this anomaly in the debugger but just when I enter the debugging tab, the problem disapears. And the values turn correct again.
I certainly don't believe in black magic, and I know that you are probably skeptic to this.
I would be too. But here is a link to a video showing it happening:
The video
Edit2:
I managed to reproduce this issue in another computer.. Both having ubuntu and using firefox (versions 20 and 21).
Edit3:
I'm sorry it took so much time! Here is a zip containing the issue. Just run it in any webserver. The code mentioned is in LaneDetection.js. Search for "HERE" in the file to find it.
https://docs.google.com/file/d/0B7y9wWiGlcYnYlo1S2pBelR1cHM/edit?usp=sharing
The problem might not happen in the first attempts. If that's the case refresh the page and try again. When the lines get horizontal you know it's there. As I said, I saw this problem happening in firefox versions 20 and 21 on ubuntu. In chrome it never happened.
By the way, I noticed that changing javascript.options.typeinference flag in firefox seems to stop the problem... I don't know exactly what that flag does, but maybe this optimization is not correctly implemented in firefox?
I can't say for sure that I actually have an answer but I think that I have confirmed that basilikum was correct to suggest a memory problem. Here's what I did: I took the first ten entries from your screenshot and calculated the correct answer. I then converted the correct answer and the wrong answer into the hexidecimal representation of the double-precision float. What I ended up with was the following:
292.416^2 = 85507.506 = 40F4E0381C71C71E
changed to 27583.373 = 40DAEFEB1C71C722
293.166^2 = 85946.694 = 40F4FBAB1C71C72A
changed to 27583.373 = 40DAEFEB1C71C722
295.818^2 = 87508.396 = 40F55D4658DC0876
changed to 28041.024 = 40DB62419637021F
294.500^2 = 86730.250 = 40F52CA400000000
changed to 27583.373 = 40DAEFEB1C71C722
297.000^2 = 88290.000 = 40F58E2000000000
changed to 28041.024 = 40DB62419637021F
221.750^2 = 49173.062 = 40E802A200000000
changed to 24964.000 = 40D8610000000000
300.300^2 = 90180.090 = 40F6044170A3D70A
changed to 28493.440 = 40DBD35C28F5C290
220.200^2 = 48488.040 = 40E7AD0147AE147B
changed to 25408.360 = 40D8D0170A3D70A4
300.600^2 = 90360.360 = 40F60F85C28F5C29
changed to 28493.440 = 40DBD35C28F5C290
213.000^2 = 45369.000 = 40E6272000000000
changed to 28032.326 = 40DB6014E5E0A72E
There's no persistent pattern to the change but there are a couple instances that are very telling of a memory issue. In the first two entries you can see that bytes 1, 2 and 3 were unchanged. In the 9th entry there's something even more odd. It would appear that bytes 0 - 3 were shifted left by exactly 4 bits! Upon considering the statement that the problem doesn't arise until after some time has passed and in light of these two anomalies, I'm fairly confident that you're encountering some sort of memory issue. Could it be, dare I say, a stack overflow?
I'm working on a JavaScript game that involves throwing a snowball. I need the snowball to render as often as possible during its flight path. Chrome does all the calculations, including setting the style.left and style.top properties, but doesn't actually redraw the snowball until it reaches its destination. Opera doesn't have this problem.
A relevant point is that putting in an alert() after renderSnowball() fixes the problem, except using the alert() is an obvious issue.
Here's my code so far:
function throwSnowball()
{
var theta = parseFloat(angleField.value) * Math.PI/180 ;
var Vir = parseFloat(velocityField.value) ;
if (!isNaN(Vir) && !isNaN(theta) )
{
Vix = Math.cos(theta) * Vir * 50;
Viy = Math.sin(theta) * Vir * 50;
time = new Date() ;
var timeThrown = time.getTime() ;
while (snowballPosY > 0)
{
current = new Date() ;
var currentTime = current.getTime() ;
var timeElapsed = (currentTime - timeThrown)/5000 ;
snowballPosX += Vix * timeElapsed;
snowballPosY += Viy * timeElapsed;
Viy -= GRAVITY * timeElapsed ;
renderSnowball() ; //renderSnowball() sets the style.left
// and style.top properties to snowballPosX pixels
// and snowballPosY pixels respectively
timeThrown = currentTime ;
}
snowballPosX = 0 ;
snowballPosY = 50 ;
renderSnowball() ;
}
}
You're totally blocking the main thread. Have you tried using a setTimeout (even with a zero timeout) to allow other things to happen during your animation?
If you're willing to use experimental technology, requestAnimationFrame would be even better.
Edit: the setTimeout approach would look something like this (replacing the while loop):
var drawAndWait = function() {
if (snowballPosY > 0) {
// movement/drawing code here
setTimeout(drawAndWait, 20 /* milliseconds */);
} else {
// reset code that would normally go after your while loop
}
};
drawAndWait();
So each time the drawing finishes, it arranges for itself to be invoked again, if appropriate. Note that your throwSnowball function will return quickly; the throwing isn't actually done until later on. This takes awhile to get used to doing correctly; don't be too concerned if it's not intuitive at first.
Try getting out of the tight loop. Chrome may not want to redraw until your function exits. Try using setInterval or setTimeout to give Chrome a chance to repaint.
I was thinking of making a game using javascript for the game logic and the HTML5 canvas element to animate the drawing. My goal is to write something that works in browsers and on newer smartphones. So I wrote up a quick program that moves 100 circles around on the screen and shows me the frame rate. I was fairly disappointed with the results:
Chrome: ~90 FPS
Firefox: ~ 25 FPS
iPhone: ~11 FPS
This was a pretty simple test so I don't like my chances when it comes to actually making a complete game. Is this the standard result from the canvas element or are there some tricks to make drawing faster, if you have any good links let me know? Is canvas just a toy at this point or can it be used for real world applications.
Edit Here's the code:
var ctx;
var width;
var height;
var delta;
var lastTime;
var frames;
var totalTime;
var updateTime;
var updateFrames;
var creats = new Array();
function init() {
var canvas =document.getElementById('main');
width = canvas.width;
height = canvas.height;
ctx = canvas.getContext('2d');
for(var i=0; i < 100; ++i) {
addCreature();
}
lastTime = (new Date()).getTime();
frames = 0;
totalTime = 0;
updateTime = 0;
updateFrames =0;
setInterval(update, 10);
}
function addCreature() {
var c = new Creature(Math.random()*100,Math.random()*200);
creats.push(c);
}
function update() {
var now = (new Date()).getTime();
delta = now-lastTime;
lastTime = now;
totalTime+=delta;
frames++;
updateTime+=delta;
updateFrames++;
if(updateTime > 1000) {
document.getElementById('fps').innerHTML = "FPS AVG: " + (1000*frames/totalTime) + " CUR: " + (1000*updateFrames/updateTime);
updateTime = 0;
updateFrames =0;
}
for(var i=0; i < creats.length; ++i) {
creats[i].move();
}
draw();
}
function draw() {
ctx.clearRect(0,0,width,height);
creats.forEach(drawCreat);
}
function drawCreat(c,i,a) {
if (!onScreen(c)) {
return;
}
ctx.fillStyle = "#00A308";
ctx.beginPath();
ctx.arc(c.x, c.y, 10, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
function onScreen(o) {
return o.x >= 0 && o.y >= 0 && o.x <= width && o.y <=height;
}
function Creature(x1,y) {
this.x = x1;
this.y = y;
this.dx = Math.random()*2;
this.dy = Math.random()*2;
this.move = function() {
this.x+=this.dx;
this.y+=this.dy;
if(this.x < 0 || this.x > width) {
this.dx*=-1;
}
if(this.y < 0 || this.y > height) {
this.dy*=-1;
}
}
}
init();
In order to make animations more efficient, and to synchronize your framerate with the UI updates, Mozilla created a mozRequestAnimationFrame() function that is designed to remove the inefficiencies of setTimeout() and setInterval(). This technique has since been adopted by Webkit for Chrome only.
In Feb 2011 Paul Irish posted a shim that created requestAnimFrame(), and shortly afterwards Joe Lambert extended it by restoring the "timeout" and "interval" delay to slow down animation ticks.
Anyway, I've used both and have seen very good results in Chrome and Firefox. The shim also fallsback to setTimeout() if support requestAnimationFrame() is unavailable. Both Paul and Joe's code is online at github.
Hope this helps!
It's largely dependent on the JavaScript engine. V8 (Chrome) and Carakan (Opera) are probably the two fastest production-quality engines. TraceMonkey (Firefox) and SquirrelFish (Safari) are far behind, with KJS bringing up the rear. This will change as hardware acceleration enters the mainstream.
As for specific optimizations, we'd probably have to see some code. Remember that the canvas supports compositing, so you really only need to redraw areas that changed. Perhaps you should re-run your benchmark without the canvas so you know if the drawing operations really were the limiting factor.
If you want to see what can be done now, check out:
js1k
Bespin
Canvas-stein
Arcs are math-intensive to draw. You can dramatically improve performance by using drawImage or even putImageData instead of drawing the path each frame.
The image can be a file loaded from a URL or it can be an image created by drawing on a separate canvas not visible to the user (not connected to the DOM). Either way, you'll save a ton of processor time.
I have written a simple bouncing ball which gives you points if you click it.
It works fine in Firefox, Safari, Chrome and on the iPad. However, the iPhone 3G/3GS were horribly slow with it. Same goes for my older Android phone.
I am sorry but I do lack specific numbers.
Chrome is the only browser thus far that I've seen high framerate results with.
You might also want to try the latest Preview of IE9. That should give you a decent benchmark of how the next generation of browsers (with hardware acceleration for HTML5) will handle your code.
So far, I've seen that IE9, Chrome 7, and Firefox 4 will all sport some form of hardware acceleration.
There's loads of optimizations to be done with Canvas drawing.
Do you have example code you could share?