Drawing successive frames to an HTML5 Canvas only shows the last frame - javascript

I have this complicated loop that calculates various still frames of what I want to show in a canvas element. Each time the frame is calculated it gets displayed I call a timer and wait till I clear it and then the next frames is displayed and so on.
drawing(transform(alone, Box, canvasx.width, canvasx.height), false, "00f", canvasx);
drawing(transform(Lines, Box, canvasx.width, canvasx.height), false, "ff0000", canvasx);
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}
while (curDate - date < 550);
if (alone.length > 0) {
canvasx.width = canvasx.width;
}
if i put a break point in var date line and press play each time, every individual frame get displayed but when I let it run through the canvas is empty while it runs and at the end it displays the last frame.
now if i delete the canvasx.width = canvasx.width; I still get the same behavior only obviously at the end i get all frames drawn one on top of the other.
Obviously its not an animation so i cant call drawing in a setinterval.
does anyone has any idea why;

You have to use setTimeout instead of your loop.
// JavaScript is single thread, and this BLOCKS the re-rendering and display of the canvas
do {
curDate = new Date();
}
while (curDate - date < 550);
Also I'd rather use context.clearRect (See MDC) instead of the resizing, there's no performance difference whatsoever.
Something along these lines should do it:
function drawCanvas() {
if (alone.length > 0) {
// clear canvas
}
drawing(transform(alone, Box, canvasx.width, canvasx.height), false, "00f", canvasx);
drawing(transform(Lines, Box, canvasx.width, canvasx.height), false, "ff0000", canvasx);
setTimeout(drawCanvas, 550);
}
Never try to emulate sleep in JavaScript you will block the whole Browser from doing anything while you're sleeping.

Related

How to "animate" changes in an ASCII art grid, one node at a time, without freezing the browser?

I have an ASCII art "pathfinding visualizer" which I am modeling off of a popular one seen here. The ASCII art displays a n by m size board with n*m number of nodes on it.
My current goal is to slowly change the appearance of the text on the user-facing board, character by character, until the "animation" is finished. I intend to animate both the "scanning" of the nodes by the pathfinding algorithm and the shortest path from the start node to the end node. The animation, which is just changing text in a series of divs, should take a few seconds. I also plan to add a CSS animation with color or something.
Basically the user ends up seeing something like this, where * is the start node, x is the end node, and + indicates the path:
....
..*.
..+.
.++.
.x..
.... (. represents an empty space)
After doing some research on both setTimeout, promises and other options, I can tell you:
JavaScript really isn't designed to allow someone to delay code execution in the browser.
I've found lots of ways to freeze the browser. I also tried to set a series of promises set to resolve after setTimeout(resolve, milliseconds) occurs, where the milliseconds steadily increases (see below code). My expectation was that numOfAnimationsForPath number of promises would be set and trigger a change in the appearance of the board when each one resolved (forming the appearance of a path). But, they all seem to resolve instantly (?) as I see the path show as soon as I click the "animate" button
const shortestPathAndScanningOrder = dijkstras(grid);
promisesRendering(1000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function promisesRendering(animationDelay, algoPath, scanTargets) {
const numOfAnimationsForScanning = scanTargets.length;
const numOfAnimationsForPath = algoPath.length;
for (let i = 1; i < numOfAnimationsForPath - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
renderAfterDelay(animationDelay * i).then(renderNode(xCoordinate, yCoordinate, "path"))
}
}
function renderAfterDelay(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
function renderNode(x, y, type) {
if (type === "scan") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = VISITED_NODE;
} else if (type === "path") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = SHORTEST_PATH_NODE;
} else {
throw "passed incorrect parameter to 'type' argument"
}
}
In my other attempt, I tried to generate pathLength number of setTimeouts as in:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
for (let i = 1; i < numOfAnimations - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
setTimeout(i * animationDelay, updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate))
}
}
...but this also resulted in the path being "animated" instantly instead of over a few seconds as I want it to be.
I believe what I want is possible because the Pathfinding Visualizer linked at the start of the post animates its board slowly, but I cannot figure out how to do it with text.
So basically:
If anyone knows how I can convince my browser to send an increasing delay value a series of function executions, I'm all ears...
And if you think it can't be done, I'd like to know that too in the comments, just so I know I have to choose an alternative to changing the text slowly.
edit: a friend tells me setTimeout should be able to do it... I'll update this w/ a solution if I figure it out
Edit2: Here is the modified version of #torbinsky's code that ended up doing the job for me...
function renderByTimer(algoPath, scanTargets) {
const numOfAnimations = algoPath.length - 1; // - 1 because we don't wanna animate the TARGET_NODE at the end
let frameNum = 1;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn() {
if (frameNum >= numOfAnimations) {
// end recursion
console.log("Done!")
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
frameNum = frameNum + 1;
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(function () {
renderIn(1000)
}, 1000);
}
// Render first frame
renderIn()
}
Thanks #torbinsky!
This should absolutely be doable using setTimeout. Probably the issue is that you are immediately registering 10,000 timeouts. The longer your path, the worse this approach becomes.
So instead of scheduling all updates right away, you should use a recursive algorithm where each "frame" schedules the timeout for the next frame. Something like this:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn(msToNextFrame, frameNum){
if(frameNum >= numOfAnimations){
// end recursion
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(msToNextFrame, function(){
renderIn(msToNextFrame, frameNum + 1)
});
}
// Render first frame
renderIn(1000, 1)
}
Note: I wrote this code in the StackOverflow code snipppet. So I was not able to test it as I did not have the rest of your code to fully run this. Treat it more like pseudo-code even though it probably works ;)
In any case, the approach I've used is to only have 1 timeout scheduled at any given time. This way you don't overload the browser with 1000's of timeouts scheduled at the same time. This approach will support very long paths!
This is a general animation technique and not particularly unique to ASCII art except that old-school ASCII art is rendered one (slow) character at a time instead of one fast pixel frame at a time. (I'm old enough to remember watching ASCII "movies" stream across hard-wired Gandalf modems at 9600bps to a z19 terminal from the local mainframe...everything old is new again! :) ).
Anyhow, queueing up a bunch of setTimeouts is not really the best plan IMO. What you should be doing, instead, is queueing up the next event with either window.requestAnimationFrame or setTimeout. I recommend rAF because it doesn't trigger when the browser tab is not showing.
Next, once you're in the event, you look at the clock delta (use a snapshot of performance.now()) to figure what should have been drawn between "now" and the last time your function ran. Then update the display, and trigger the next event.
This will yield a smooth animation that will play nicely with your system resources.

javascript web audio analyser : getByteFrequencyData at a precise time?

Based on the web audio analyser API, I am creating an audio animation that draws images based on the real time frequency spectrum (like the classical bar graphics that move to the frequency of the sound, except that it is not bars that are drawn but something more complex).
It works fine, my only issue is that I am not able to stop the image at a precise time.
When I want to have it stopped at let's say 5 seconds, then some times it stops at 5.000021, or 5.000013, or 5.0000098, ...
and the problem is that the frequency spectrum (and so my image based on this frequency spectrum) is not the same at 5.000021, or 5.000013, or 5.0000098, ...
This means that the user when he wants to see the image corresponding to 5s, every time he sees a slightly different image, and I would like to have only one image corresponding to 5s (often the image is only slightly different at every try, but sometimes the differences are quite huge).
Here are extracts of my code:
var ctx = new AudioContext();
var soundmp3 = document.getElementById('soundmp3');
soundmp3.src = URL.createObjectURL(this.files[0]);
var audioSrc = ctx.createMediaElementSource(soundmp3);
var analyser = ctx.createAnalyser();
analyser.fftSize = 2048;
analyser.smoothingTimeConstant = 0.95;
audioSrc.connect(analyser);
audioSrc.connect(ctx.destination);
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
function renderFrame() {
if(framespaused) return;
drawoneframe();
requestAnimationFrame(renderFrame);
};
function drawoneframe(){
analyser.getByteFrequencyData(frequencyData);
// drawing of my image ...
};
function gotomp3(timevalue){
soundmp3.pause();
newtime = timevalue;
backtime = newtime - 0.2000;
soundmp3.currentTime = backtime;
soundmp3.play();
function boucle(){
if(soundmp3.currentTime >= timevalue){
if(Math.abs(soundmp3.currentTime-newtime) <= 0.0001){
drawoneframe();
soundmp3.pause();
soundmp3.currentTime = timeatgetfrequency;
return;
} else {
soundmp3.pause();
soundmp3.currentTime = backtime;
soundmp3.play();
};
}
setTimeout(boucle, 1);
};
boucle();
};
document.getElementById("fieldtime").onkeydown = function (event) {if (event.which == 13 || event.keyCode == 13) {
gotomp3(document.getElementById("fieldtime").value);
return false;
}
return true;
};
Code explanation: if the user enters a value in the "fieldtime" (= newtime) and hits enter, then the I go first 0.2s back, start playing and stop when the currentTime is very close to newtime (I have to go back first, because when I go directly to newtime and stop immediately afterwards then the analyser.getByteFrequencyData does not yet have the values at newtime). With boucle() I manage to get it stopped at very precise times: if newtime = 5, then the time when drawoneframe(); is called is 5.000xx but the problem is that every time the user enters 5 as newtime, the image that is shown is slightly different.
So my question: has someone an idea how I could achieve that every time the user enters the same time as newtime, the image will be exactly the same ?
I am not quite aware at which times the soundmp3.currentTime is updated ? With a samplerate of 44.1kHz, I guess it is something like every 0.0227ms, but does this mean that it is updated exactly at 0, 0.0227ms, 0.0454ms, ...or just approximately ?
I thought about smoothing the analyser results, so that there are less variations for small time variations. Setting analyser.smoothingTimeConstant to a value close to 1 is not sufficient. But maybe there is another way to do it.
I do not need high precision results, I just would like that if a user wants to see the image corresponding to x seconds, then each time he enters x seconds, he sees exactly the same image.
Thank you very much for any help.
Mireille

Make an image appear and then grow in size in a html canvas

I was wondering if its possible to create an image using various lines and bez-curves that appears and then moves down the canvas and gets larger as it grows. The animation in question requires the room to fill with gas and so the the gas gets greater and greater as it moves around the canvas.
I had thought about just drawing the image and then using a for loop to move the image down until a certain y coordinate, but this doesnt help with the increasing part of the created image
Any help appreciated
There are different ways to make animations happen. What I normally do for my canvas games is that I have a gameloop that loops at a certain FPS that I decide at the start of my project. Each animation is normally controlled by time an speed.
var fps = 60;
var lastUpdateTime = +new Date(); //when did I last update the game?
function gameloop() {
var updateStartTime = +new Date();
update(updateStartTime-lastUpdateTime); //update the game for the according to the elapsed time since last update
lastUpdateTime = updateStartTime;
//Normally I also handle spawning stuff here
//I also remove old object in my gameloop
setTimeout(gameloop,1000/fps)
}
function update(elapsedTime) {
//This function update the locations of game elements such as player position, bullets, enemies, bananas etc. (or even gas clouds!)
//When I change a value, I use the time-parameter passed along with the call so that I get a smooth game even though the browser might lag.
playerX += velocity*timeElapsed; //as an example
}
Here's an example on your problem where I made a function to create a gas bubble object (yes, I do know how wrong it is to use gas bubbles =P). These objects travel downwards (physics?!) and increase in size (OK, this is starting to sound more and more crazy), just have a look:
http://jsfiddle.net/Niddro/ppa4xuw8/

HTML5 Canvas game loop delta time calculations

I'm new to game development. Currently I'm doing a game for js13kgames contest, so the game should be small and that's why I don't use any of modern popular frameworks.
While developing my infinite game loop I found several articles and pieces of advice to implement it. Right now it looks like this:
self.gameLoop = function () {
self.dt = 0;
var now;
var lastTime = timestamp();
var fpsmeter = new FPSMeter({decimals: 0, graph: true, theme: 'dark', left: '5px'});
function frame () {
fpsmeter.tickStart();
now = window.performance.now();
// first variant - delta is increasing..
self.dt = self.dt + Math.min(1, (now-lastTime)/1000);
// second variant - delta is stable..
self.dt = (now - lastTime)/16;
self.dt = (self.dt > 10) ? 10 : self.dt;
self.clearRect();
self.createWeapons();
self.createTargets();
self.update('weapons');
self.render('weapons');
self.update('targets');
self.render('targets');
self.ticks++;
lastTime = now;
fpsmeter.tick();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
So the problem is in self.dt I've eventually found out that first variant is not suitable for my game because it increases forever and the speed of weapons is increasing with it as well (e.g. this.position.x += (Math.cos(this.angle) * this.speed) * self.dt;..
Second variant looks more suitable, but does it correspond to this kind of loop (http://codeincomplete.com/posts/2013/12/4/javascript_game_foundations_the_game_loop/)?
Here' an implementation of an HTML5 rendering system using a fixed time step with a variable rendering time:
http://jsbin.com/ditad/10/edit?js,output
It's based on this article:
http://gameprogrammingpatterns.com/game-loop.html
Here is the game loop:
//Set the frame rate
var fps = 60,
//Get the start time
start = Date.now(),
//Set the frame duration in milliseconds
frameDuration = 1000 / fps,
//Initialize the lag offset
lag = 0;
//Start the game loop
gameLoop();
function gameLoop() {
requestAnimationFrame(gameLoop, canvas);
//Calcuate the time that has elapsed since the last frame
var current = Date.now(),
elapsed = current - start;
start = current;
//Add the elapsed time to the lag counter
lag += elapsed;
//Update the frame if the lag counter is greater than or
//equal to the frame duration
while (lag >= frameDuration){
//Update the logic
update();
//Reduce the lag counter by the frame duration
lag -= frameDuration;
}
//Calculate the lag offset and use it to render the sprites
var lagOffset = lag / frameDuration;
render(lagOffset);
}
The render function calls a render method on each sprite, with a reference to the lagOffset
function render(lagOffset) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
sprites.forEach(function(sprite){
ctx.save();
//Call the sprite's `render` method and feed it the
//canvas context and lagOffset
sprite.render(ctx, lagOffset);
ctx.restore();
});
}
Here's the sprite's render method that uses the lag offset to interpolate the sprite's render position on the canvas.
o.render = function(ctx, lagOffset) {
//Use the `lagOffset` and previous x/y positions to
//calculate the render positions
o.renderX = (o.x - o.oldX) * lagOffset + o.oldX;
o.renderY = (o.y - o.oldY) * lagOffset + o.oldY;
//Render the sprite
ctx.strokeStyle = o.strokeStyle;
ctx.lineWidth = o.lineWidth;
ctx.fillStyle = o.fillStyle;
ctx.translate(
o.renderX + (o.width / 2),
o.renderY + (o.height / 2)
);
ctx.beginPath();
ctx.rect(-o.width / 2, -o.height / 2, o.width, o.height);
ctx.stroke();
ctx.fill();
//Capture the sprite's current positions to use as
//the previous position on the next frame
o.oldX = o.x;
o.oldY = o.y;
};
The important part is this bit of code that uses the lagOffset and the difference in the sprite's rendered position between frames to figure out its new current canvas position:
o.renderX = (o.x - o.oldX) * lagOffset + o.oldX;
o.renderY = (o.y - o.oldY) * lagOffset + o.oldY;
Notice that the oldX and oldY values are being re-calculated each frame at the end of the method, so that they can be used in the next frame to help figure out the difference.
o.oldX = o.x;
o.oldY = o.y;
I'm actually not sure if this interpolation is completely correct or if this is best way to do it. If anyone out there reading this knows that it's wrong, please let us know :)
The modern version of requestAnimationFrame now sends in a timestamp that you can use to calculate elapsed time. When your desired time interval has elapsed you can do your update, create and render tasks.
Here's example code:
var lastTime;
var requiredElapsed = 1000 / 10; // desired interval is 10fps
requestAnimationFrame(loop);
function loop(now) {
requestAnimationFrame(loop);
if (!lastTime) { lastTime = now; }
var elapsed = now - lastTime;
if (elapsed > requiredElapsed) {
// do stuff
lastTime = now;
}
}
This isn't really an answer to your question, and without knowing more about the particular game I can't say for sure if it will help you, but do you really need to know dt (and FPS)?
In my limited forays into JS game development I've found that often you don't really need to to calculate any kind of dt as you can usually come up with a sensible default value based on your expected frame rate, and make anything time-based (such as weapon reloading) simply work based on the number of ticks (i.e. a bow might take 60 ticks to reload (~1 second # ~60FPS)).
I usually use window.setTimeout() rather than window.requestAnimationFrame(), which I've found generally provides a more stable frame rate which will allow you to define a sensible default to use in place of dt. On the down-side the game will be more of a resource hog and less performant on slower machines (or if the user has a lot of other things running), but depending on your use case those may not be real concerns.
Now this is purely anecdotal advice so you should take it with a pinch of salt, but it has served me pretty well in the past. It all depends on whether you mind the game running more slowly on older/less powerful machines, and how efficient your game loop is. If it's something simple that doesn't need to display real times you might be able to do away with dt completely.
At some point you will want to think about decoupling your physics from your rendering. Otherwise your players could have inconsistent physics. For example, someone with a beefy machine getting 300fps will have very sped up physics compared to someone chugging along at 30fps. This could manifest in the first player cruising around in a mario-like scrolling game at super speed and the other player crawling at half speed (if you did all your testing at 60fps). A way to fix that is to introduce delta time steps. The idea is that you find the time between each frame and use that as part of your physics calculations. It keeps the gameplay consistent regardless of frame rate. Here is a good article to get you started: http://gafferongames.com/game-physics/fix-your-timestep/
requestAnimationFrame will not fix this inconsistency, but it is still a good thing to use sometimes as it has battery saving advantages. Here is a source for more info http://www.chandlerprall.com/2012/06/requestanimationframe-is-not-your-logics-friend/
I did not check the logic of the math in your code .. however here what works for me:
GameBox = function()
{
this.lastFrameTime = Date.now();
this.currentFrameTime = Date.now();
this.timeElapsed = 0;
this.updateInterval = 2000; //in ms
}
GameBox.prototype.gameLoop = function()
{
window.requestAnimationFrame(this.gameLoop.bind(this));
this.lastFrameTime = this.currentFrameTime;
this.currentFrameTime = Date.now();
this.timeElapsed += this.currentFrameTime - this.lastFrameTime ;
if(this.timeElapsed >= this.updateInterval)
{
this.timeElapsed = 0;
this.update(); //modify data which is used to render
}
this.render();
}
This implementation is idenpendant from the CPU-speed(ticks). Hope you can make use of it!
A great solution to your game engine would be to think in objects and entities. You can think of everything in your world as objects and entities. Then you want to make a game object manager that will have a list of all your game objects. Then you want to make a common communication method in the engine so game objects can make event triggers. The entities in your game for example a player would not need to inherent from anything to get the ability to render to the screen or have collision detection. You would simple make common methods in the entity that the game engine is looking for. Then let the game engine handle the entity as it would like. Entities in your game can be created or destroyed at anytime in the game so you should not hard-code any entities at all in the game loop.
You will want other objects in your game engine to respond to event triggers that the engine has received. This can be done using methods in the entity that the game engine will check to see if the method is available and if it is would pass the events to the entity. Do not hard code any of your game logic into the engine it messes up portability and limits your ability to expand on the game later on.
The problem with your code is first your calling different objects render and updates not in the correct order. You need to call ALL your updates then call ALL your renders in that order. Another is your method of hard coding objects into the loop is going to give you a lot of problems, when you want one of the objects to no longer be in the game or if you want to add more objects into the game later on.
Your game objects will have an update() and a render() your game engine will look for that function in the object/entity and call it every frame. You can get very fancy and make the engine work in a way to check if the game object/entity has the functions prior to calling them. for example maybe you want an object that has an update() but never renders anything to the screen. You could make the game object functions optional by making the engine check prior to calling them. Its also good practice to have an init() function for all game objects. When the game engine starts up the scene and creates the objects it will start by calling the game objects init() when first creating the object then every frame calling update() that way you can have a function that you only run one time on creation and another that runs every frame.
delta time is not really needed as window.requestAnimationFrame(frame); will give you ~60fps. So if you're keeping track of the frame count you can tell how much time has passed. Different objects in your game can then, (based off of a set point in the game and what the frame count was) determine how long its been doing something based off its new frame count.
window.requestAnimationFrame = window.requestAnimationFrame || function(callback){window.setTimeout(callback,16)};
gameEngine = function () {
this.frameCount=0;
self=this;
this.update = function(){
//loop over your objects and run each objects update function
}
this.render = function(){
//loop over your objects and run each objects render function
}
this.frame = function() {
self.update();
self.render();
self.frameCount++;
window.requestAnimationFrame(frame);
}
this.frame();
};
I have created a full game engine located at https://github.com/Patrick-W-McMahon/Jinx-Engine/ if you review the code at https://github.com/Patrick-W-McMahon/Jinx-Engine/blob/master/JinxEngine.js you will see a fully functional game engine built 100% in javascript. It includes event handlers and permits action calls between objects that are passed into the engine using the event call stack. check out some of the examples https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/master/examples where you will see how it works. The engine can run around 100,000 objects all being rendered and executed per frame at a rate of 60fps. This was tested on a core i5. different hardware may vary. mouse and keyboard events are built into the engine. objects passed into the engine just need to listen for the event passed by the engine. Scene management and multi scene support is currently being built in for more complex games. The engine also supports high pixel density screens.
Reviewing my source code should get you on the track for building a more fully functional game engine.
I would also like to point out that you should have requestAnimationFrame() called when you're ready to repaint and not prior (aka at the end of the game loop). One good example why you should not call requestAnimationFrame() at the beginning of the loop is if you're using a canvas buffer. If you call requestAnimationFrame() at the beginning, then begin to draw to the canvas buffer you can end up having it draw half of the new frame with the other half being the old frame. This will happen on every frame depending on the time it takes to finish the buffer in relation to the repaint cycle (60fps). But at the same time you would end up overlapping each frame so the buffer will get more messed up as it loops over its self. This is why you should only call requestAnimationFrame() when the buffer is fully ready to draw to the canvas. by having the requestAnimationFrame() at the end you can have it skip a repaint if the buffer is not ready to draw and so every repaint is drawn as it is expected. The position of requestAnimationFrame() in the game loop has a big difference.

Choppy result from DeltaTime function in JS

I'm building an HTML5 canvas game that's using requestAnimationFrame to render each frame in the canvas. To move animations at a rate based on seconds, not framerate, I am building a deltaTime function similar to that used in Unity:
var startTime = new Date().getTime();
var lastTime = null;
function deltaTime()
{
if (lastTime == null)
{
lastTime = new Date().getTime();
}
var dt = new Date().getTime() - lastTime;
lastTime = new Date().getTime();
return dt;
}
However, when I multiply this value by the animation speed, it causes a choppy result instead of a smooth animation. It appears that millisecond accuracy is not enough. How can I improve this function to produce a smooth result?
Check out the newer version of requestAnimationFrame that sends in an elapsed time that is accurate to the sub-millisecond
http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision
But that may not be the cause of your choppiness.
Using requestAnimationFrame (aka RAF) with a delta-time causes the object being animated to appear in an accurate location.
If you're animating the sun across the sky, then RAF+deltaTime will insure that whenever the sun is drawn, it will be drawn at the correct spot in the sky. It will insure accuracy at the cost of choppy sun movement.
RAF+deltaTime sacrifices smooth animation for positioning accuracy.
You don't give a code example, but the solution is to make sure your code is efficient enough to fully draw within the RAF timespan.
Alternatively, you can make the amount of movement smaller so a skipped frame will not be very noticeable to the user.
requestAnimationFrame is highly optimized because it updates the display when the system is most ready to do so. That means if you want to avoid janky animations, you need to make the animation happen inside an requestAnimationFrame loop. I'm not sure if your code is doing that, but here's how you can set the frame rate to 15 fps using requestAnimationFrame:
var fps = 15,
startTime = 0,
frameDuration = 1000 / fps;
//Start the game loop
update();
function update() {
requestAnimationFrame(update, canvas);
//Check whether the current time matches the start time
if (Date.now() >= startTime || startTime === 0){
//... run the game code ...
//Set the new start time for the next frame,
//which will be 66 milliseconds ahead of the current time
startTime = Date.now() + frameDuration;
}
}
But the biggest bottleneck to the display is usually the rendering: If you can use WebGL it's so much faster than canvas. A good rendering framework like PixiJs can help you optimize this:
https://github.com/GoodBoyDigital/pixi.js/

Categories