How do js animations work? - javascript

Im trying to understand how to make a javascript animation run smoothly and I've been reading some answers around here and I found something I don't understand.
Here is the link to the question Smooth javascript animation
In the answer with most votes it says "which is why generally it's a good idea to base position/frame on the amount of time that has elapsed since the start of the animation (using new Date().getTime()) rather than moving/changing a fixed amount each frame."
Can anyone show me a very very simple example that uses the method from this answer, and explain how you then control the speed of the animation?

Overview
Generally with animations, you have three components
Update
Draw
Timer
These are run within a loop, called the animation loop. A typical animation loop might look like the following (I will explain all of the functions in detail below):
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
timer(); // Controls the timing of when animate will be called again
};
animate(); // Start animating
The animation loop is the main flow controller of what goes on inside your animation. Basically the code inside the animation loop is called over and over again. Each execution of the animate function constitutes a frame. During a frame, your world is updated and redrawn on the screen. The frequency with which the animate function runs is called the frame rate, and is controlled by the timer.
You will also need a reference to the drawing context, which will be used to hold the elements you wish to animate, also known as sprites:
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
Update
Update is responsible for updating the status of each item you wish to animate, once per frame. To take a simplistic example, say you have an array containing three cars, each with an x and y position and a velocity. On each frame, you want to update the position of the car to reflect the distance it should travel based on its velocity.
Our cars array, and the code used to generate a car might look like this:
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y
// positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
When we call update, we will want to call the drive method of each car in order to move the car sprites around the screen. This drive method will return an html string that represents the sprite, including it's current position. We will want to append this string to a variable that can be used to set the inner HTML of the canvas div:
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
Draw
When the update function is done outputting our sprites, we will need to actually draw the elements on the canvas. In order to do this, we use the innerHTML method of the canvas variable, passing it in htmlStr, which contains markup used to represent all of the sprites. This will ask the browser to parse the text and spit DOM elements out on the screen:
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
You could argue that there are better ways to do this, and there probably are. However, in the interest of keeping it simple, I kept it simple. This is also not the most performant method, as the DOM API is very slow. If you look at the system resource usage of this application, most of it will be hogged by innerHTML calls. If you want a faster context for drawing, you should use HTML 5 canvas.
Timer
Finally, you need some way of calling animate over and over again. You could do this a couple of ways. First, there is good old setTimeout:
setTimeout(animate, 0);
This instructs the browsers to call animate after a delay of 0ms. In practice, animate will never execute after a delay of 0ms. The minimum resolution of setTimeout in most browsers is around 15ms, but even that is not guaranteed, due to the way the UI thread works in javascript.
A better solution is to use requestAnimationFrame, which basically tells the browser you are doing animation. The browser will do a bunch of nice optimizations for you. However, this is not fully supported across browsers. You should use this solution for a cross-browser requestAnimationFrame polyfill:
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Then you can use:
requestAnimFrame(animate);
In the end your finished program will look something like:
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
requestAnimFrame(animate); // Controls the timing of when animate will be called again
};
animate(); // Start animating
Conclusion
There is a lot of room for optimization, tweaking, abstraction, and a whole lot of other goodness that I didn't go into here. There are a ton of ways to implement update and draw, and all have there pros and cons.
However, every animation you will ever write will use some sort of animation loop and they all have this basic architecture. Hopefully, this illustrates the basics of an animation in javascript.

The basic idea is the following:
You want to move a DIV from 0,0 to 100,0 in 1 second.
You'd like it to have 50 frames per second (using a setTimeout(fun, 20))
However, since the callback is not guaranteed to run in exactly 20ms, your callback needs to figure out when it was actually run. For example, let's say your animation started at time X but your first animation callback wasn't called until X+5ms. You need to calculate the position that the div should be at 25ms of the animation, your animation callback can't assume 50 even steps.
Here's a very simple code sample. I don't usually code with globals but this is the easiest way to show the technique.
http://jsfiddle.net/MWWm6/2/
var duration = 1000; // in ms
var startTime; // in ms
var startX = 0;
var endX = 500;
// 0 means try to get as many frames as possible
// > 1: Remember that this is not guaranteed to run as often as requested
var refreshInterval = 0;
var div = document.getElementById('anim');
function updatePosition() {
var now = (new Date()).getTime();
var msSinceStart = now - startTime;
var percentageOfProgress = msSinceStart / duration;
var newX = (endX - startX) * percentageOfProgress;
div.style.left = Math.min(newX, endX) + "px";
if (window.console) {
console.log('Animation Frame - percentageOfProgress: ' + percentageOfProgress + ' newX = ' + newX);
}
if (newX < endX) {
scheduleRepaint();
}
}
function scheduleRepaint() {
setTimeout(updatePosition, refreshInterval);
}
div.onclick = function() {
startTime = (new Date()).getTime();
scheduleRepaint();
}

This post describes the best way to do animations: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
That will run at 60fps, or as fast as possible.
Once you know that, then you can decide how long you want your animation to take and how far the object is moving, then work out how far it has to move each frame.
Of course, for smooth animations, you should use CSS Transitions where ever possible – take a look at http://css3.bradshawenterprises.com.

The quick answer is that setTimeout doesn't guarantee that the callback will be executed n number of milliseconds after you call it. It just guarantees that it will be executed no sooner than n milliseconds from that time. John Resig covers some of this in this piece.
For this reason, you need to check what time your animation callback has actually executed, not what time you've scheduled it with setTimeout.

I've written a blogpost which demonstrates the concept of "time based" animation using canvas and a sprite sheet.
The example animates a sprite and takes into account the elapsed time to make sure it remains consistent in different framerates. You could easily apply this into moving an element around a page by changing the code slightly. The concepts related to timing and animation itself remain pretty much the same.
http://codeutopia.net/blog/2009/08/21/using-canvas-to-do-bitmap-sprite-animation-in-javascript/
(I always feel so spammy leaving answers that basically just link to my blog, even if the link is actually entirely relevant :D )

Look at it this way: You've got some object you want to move. Let's say you're giving it 5 seconds to move from A to B, and you want it to be done at 30fps. That means you've got to update the object's position 150 times, and have at most 1/30 = 0.0333 seconds per update.
If you're using the "time since last adjustment", and your code takes 0.05 seconds to execute, then you're not going to make the 30fps. However, if you're basing the animation off when it started, then it doesn't matter how long your code takes - when your update code fires, it'll calculate and set the object's position should be at that stage of the animal, regardless of how many frames have actually been displayed.

Related

How do I change a variable while using a function?

I'm coding on the p5.js Website Editor
So I'm trying to make a lot of rectangles that move when I press a specific key.
To make that I thought of making a function where I would put everything related to the rectangles moving, so that I don't have to rewrite a code to make them move every time. I want all of them to move the same way.
This is what I tried
function wall(x, y, sx, sy){
rect(x, y, sx, sy);
if(keyIsDown(65)){
return x+1;
}
}
wall(300, 300, 20, 30);
and just got a rectangle in the right coordinates but not moving when I press the "a" key
When you want to move things in p5.js, you need to use the draw() function. The draw() function runs the code inside of it repeatedly multiple times a second. You can use that to create an illusion of movement by updating the location of your object by a small amount every frame and then re-rendering the object.
I don't see you using the draw() function, so I'm guessing this is the first part of your problem.
The second part I see is that you are referring to "a lot of rectangles", but you are only making one.
Consider the code below for a quick solution (copy-paste it into the p5.js editor to see it in action).
let wallX = 200;
let wallY = 100;
let wallSpeed = 2;
let wallWidth = 20;
let wallHeight = 30;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if (keyIsDown(LEFT_ARROW)) {
wallX = wallX - wallSpeed;
} else if (keyIsDown(RIGHT_ARROW)) {
wallX = wallX + wallSpeed;
}
if (keyIsDown(UP_ARROW)) {
wallY = wallY - wallSpeed;
} else if (keyIsDown(DOWN_ARROW)) {
wallY = wallY + wallSpeed;
}
wall(wallX, wallY, wallWidth, wallHeight);
}
function wall(x, y, sx, sy) {
for (let i = 0; i < 5; i++){
rect(x, y + sy * i, sx, sy);
}
}
In this code snippet, I am defining some global variables (generally not a good practice, but for the purposes of a p5.js sketch this is fine). The important ones are the wallX and the wallY variables. These variables are defined outside of the draw() function, so they will not be reset every time the loop runs.
Inside the loop, there are key handlers defined (I used the arrow keys for clarity). If the arrow keys are pressed, the position of the wall will change by the wallSpeed value per frame.
Also, note the background(220) call - this redraws the grey background at the beginning of the loop. If you remove that, the results of the previous renders will be visible, chances are that you don't want that.
Finally, the wall() function. You can see that the key press handling is not done inside of it - it needs the state information to come from the outside. All this function does is use a loop to draw five rectangles in a stack, at the position it is given. When the position changes, it redraws the wall in a different place on the canvas. The rectangles all refer to the root x and y values, so they will all move together as a unit.
Another way would be to create the class to represent a Wall and make instances of that class and call methods on those instances, but that could be something to revisit once you gain more experience with p5.js.
Hope this helps!

Canvas Rendering Optical Illusion in JS Game (Briefly duplicated graphics) - Fix?

I'm creating a side-scrolling space-shooter in javascript. So far everything seems to be working well. However, there is an odd bug in the canvas rendering that I can't quite figure out (and is difficult to describe, so bear with me!)
I have a player that can shoot projectiles by clicking the left mouse button. When the projectile first leaves the player, there appears to be two of them for a brief second, until they eventually merge in to the one projectile. I'm not creating two, so this seems like an optical illusion (this is most evident if you fire a few projectiles in quick succession).
The really odd thing is, when I try and capture a screenshot of this happening, all looks fine. Can anyone figure out what's going on?
Player code including projectiles (full code in fiddle);
var Player = (function () {
// ------------------------------------------------------------------------------------------------
// PLAYER VARIABLES
// ------------------------------------------------------------------------------------------------
var w = 50;
var h = 50;
var x = 0;
var y = 0;
var projectiles = [];
// ------------------------------------------------------------------------------------------------
// BIND EVENTS TO THE GLOBAL CANVAS
// ------------------------------------------------------------------------------------------------
Canvas.bindEvent('mousemove', function (e) {
y = (e.pageY - Canvas.element.getBoundingClientRect().top) - (h / 2);
});
Canvas.bindEvent('click', function () {
createProjectile(50, (y + (h / 2)) - 10);
});
// ------------------------------------------------------------------------------------------------
// FUNCTIONS
// ------------------------------------------------------------------------------------------------
var createProjectile = function (x, y) {
projectiles.push({
x: x,
y: y
})
};
var update = function () {
for (var p = projectiles.length - 1; p >= 0; p--) {
projectiles[p].x += 10;
if (projectiles[p].x > Canvas.element.width)projectiles.splice(p, 1);
}
};
var render = function () {
Canvas.context.fillStyle = 'white';
Canvas.context.fillRect(x, y, w, h);
for (var p = 0; p < projectiles.length; p++) {
Canvas.context.fillStyle = 'red';
Canvas.context.fillRect(projectiles[p].x, projectiles[p].y, 5, 5);
}
};
// ------------------------------------------------------------------------------------------------
// Exposed Variables and Functions
// ------------------------------------------------------------------------------------------------
return {
update: update,
render: render
}
})();
Js Fiddle Demo HERE: https://jsfiddle.net/oqz204bj/
EDIT
Based on #Pimskie's answer, It does indeed seem like an optical illusion - so my question now becomes, how could I reduce this effect? I plan on implementing a feature in the future that allows the player to switch weapons (where some of them would **actually* fire multiple projectiles) but I don't want this effect to remain for fear of confusion.
yes it is an optical illusion. The reason it looks like there multiple squares when first fired is because your eyes are focused on the big static ship square. Once your eye starts to follow the movement path, then it looks more like a fluid square moving instead of a square being redrawn 60 or 30 times per second. hold a piece of paper or your hand up to your screen covering the left half of it. Focus on the piece of paper and fire a few shots. You'll notice that the shots seem to appear multiple, the same as when just fired. It's a matter of your mind seeing 3 different frames as the same one.
requestAnimationFrame depends on the frame rate of your browser and computer. In most cases that's 60fps. 60 to 70fps is the limit of most monitors, and so it doesn't make sense to try and go above that. HOWEVER you can create the illusion of a more fluid movement by having a trailing tracer effect on your projectiles. That would involve having 2 or 3 extra squares created behind each projectile that have less and less opacity.
My best guess it's an optical illusion indeed.
Check this updated fiddle: https://jsfiddle.net/oqz204bj/1/
I removed one requestAnimationFrame and replaced a other with a very slow setInterval, just for demonstration. You can see only one bullet is created.

Three.js / ShaderParticleEngine — SPE.Group.tick bad delta argument?

I'm using the ShaderParticleEngine library for Three.js to create particle emitters.
I picked several code snippets on the Internet to have a working emitter.
Firstly I believed that is wasn't working.
But in fact, the emitter was displayed on the map, but a single motionless particle was on the screen.
After some debugging I undestood that the particle was moving but infinitely slowly. I need to use tick(delta * 1000) to see the emitter in action. And the result is quite ugly (full of gaps, alone particles).I have no problem of low FPS.
The only solution I found is to remove delta argument in the tick function call: particleGroup.tick().
The result is better but is still deceiving, judge by yourself:
Online Emitter Editor:
My result:
I can't understand. I use the same code proposed in the library examples and I use the export feature in the emitter editor.
If I try other variations (eg. on particle life/velocity) I get a very different result in my game, maybe the particle life is not computed correctly because delta argument isn't given?
My game loop:
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
Delta value loop by loop:
30.0000010000003385357559
9.999985195463523e-7
30.0000020000006770715117
0.0000010000003385357559
30.0000020000006770715117
0.0000010000003385357559
0.0000020000006770715117
30.0000010000003385357559
0.000002999999196617864
0.0000010000003385357559
9.999985195463523e-7
0.000002999999196617864
0.0000010000003385357559
0.000001999998858082108
0.0000010000003385357559
20.0000020000006770715117
9.999985195463523e-7
0.0000010000003385357559
To solve smoothness, try the following:
function makeSmoothSPETick(simulator, timeDelta, maxSubstepSize){
var numSubsteps = Math.floor(timeDelta/maxSubstepSize);
var leftOverTime = timeDelta%maxSubstepSize;
while(numSubsteps-->0){
simulator.tick(maxSubstepSize);
}
if(leftOverTime> 0){
//handle the rest
simulator.tick(leftOverTime);
}
}
If you use this function in your code - it will allow you to essentially subdivide steps that are too large into smaller ones of fixed size. As SquareFeet pointed out, say 16ms for 60FPS - you could use something like this:
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
makeSmoothSPETick(particleGroup, delta, 0.016);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
You should get results visually similar to what you'd expect if you were running at smooth 60fps. Beware though, if target hardware can't handle these substeps - you may need to get more logic into your solver algorithm. I'd suggest keeping statistics for past 100 frames or so, and using that to decide how much you can split your incoming step value.
EDIT:
To make sure your timing isn't getting mangled, please try the following:
var lastFrameTime = Date.now()/1000;
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = Date.now()/1000; //getting current time in seconds since epoch
delta = time-lastFrameTime;
lastFrameTime = time;
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
I hope posting this as an answer is okay...
I've bumped the particle engine up a minor version to 0.7.7, having implemented a fix for your issue of "not-very-smooth-looking" emitters.
What was happening before was this:
SPE.Emitter.tick() called with a dt value
This tick function determines how many particles should be marked alive based on the dt argument passed to it. For larger dt values, more particles are marked as alive, for smaller values fewer are marked as alice.
The emitter then resets these particles and waits for the next call.
Assuming more than one particle is going to be marked as alive per frame, and they all originate at the same position in space, then all the particles will be at the same place when they're activated. This is why you saw some "clumping" happening.
What happens now is this:
SPE.Emitter.tick() called with a dt value, just as before.
The tick function now determines how many particles should be marked as alive, and whilst marking them so, sets each particles age to be a fraction of the dt value passed in.
So (!), assuming 100 particles are emitted per frame, and a dt value of 0.016 is passed to the emitter's tick function, each of those 100 particles that will be marked as alive is assigned an age value of (0.016 / 100) * i where i is the particle index (in this case, a value of 0 to 100).
I hope that makes sense. You can see the changes here: https://github.com/squarefeet/ShaderParticleEngine/blob/master/src/ShaderParticleEmitter.js#L240-L246
Master branch has been updated.

Image doesn't move on canvas

I'm new. I was trying to move an image but it just doesn't, I don't know where is the problem. I checked some topics but it didn't work., well here's my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var bgImage = new Image();
var player = new Image();
var x = 50;
var y = 50;
// We draw the background
bgImage.onload = function() {
context.drawImage(bgImage, 0, 0);
};
bgImage.src ='images/bg.png';
// We draw the player
player.onload = function(){
context.drawImage(player, x, y);
};
player.src = "images/player.png";
addEventListener("keypress", move,true);
function move(e){
if (e.keyCode == 39){
x += 2;
}
if (e.keyCode == 37){
x -= 4;
}
if (e.keyCode == 38){
y -= 4;
}
if (e.keyCode == 40){
y += 4;
}
}
You're missing the concept of the thing here, canvas is not dynamic by itself, it only provides you with the ability to draw static image frames. Therefore, the animation and interaction is possible, but it has to be entirely coded by yourself.
Any animation effect is given through the drawing of a continuous series of static frames over one another. Each frame will graphicaly represent a slight change in position/form/color of one or various objects within the scene.
It becomes clear you'd have to setup a loop chain where any change made to properties of an object in the scene would be reflected in the image frame which is currently on screen.
So it's not enough to change the value of your x and y variables, because they have been used only once in your code, to draw the first frame, then, they are used nowhere. There is nothing being drawn after the first frame. That one static image you created will remain forever on the screen, you can't expect animation out of a single static image.
Fix-wise, it's firstly necessary to setup a loading scheme for your image resources, since you need more than one. Because their loading is asynchronous AND out of order (they do not load respecting the order they were created).
After everything is loaded, you can choose to either fire a continuous loop, which will draw another frame regardless of wheter any change has actually been made to anything on the screen (this is the standard way for making animations, in more complex applications, it will allow you to keep control of animation time)
Or, since your code is simple enough, you could choose to draw another frame when, and only when, the position of your object has been changed. Something like this
addEventListener("keypress", move,true);
function move(e)
{
//... key handling stuff
redraw(); // Drawing the next, modified, frame
}
function redraw()
{
context.clearRect(0, 0, canvas.width, canvas.height)
context.drawImage(bgImage, 0, 0);
context.drawImage(player, x, y);
}

HTML Canvas Interval vs RequestAnimationFrame

So, maybe total brainfart here. The syntax for setInterval() is pretty clear. Do something every x miliseconds. How is this best translated to using the requestAnimationFrame() ?
I have about 300 objects and each is supposed to perform an animation sequence at a certain interval (every 8, 6, 2, etc seconds)? How can I best accomplish this using requestAnimationFrame() which gets called ~60 times a second? There is probably an easy answer, I just, for the life of me, can't figure it out.
To force requestAnimationFrame to stick to a specific FPS you can use both at once!
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimationFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
A little weird, but noth the most confusing thing in the world.
You can also use requestAnimationFrame not with FPS but with elapsed time in order to draw objects that need to be updated based on the time difference since the last call:
var time;
function draw() {
requestAnimationFrame(draw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now;
// Drawing code goes here... for example updating an 'x' position:
this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}
These two snippets are from this fine article, which contains additional details.
Good question by the way! I don't think I've seen this answered on SO either (and I'm here way too much)
requestAnimationFrame is pretty low level, it just does what you already said: roughly gets called at 60fps (assuming the browser can keep up with that pace). So typically you would need to build something on top of that, much like a game engine that has a game loop.
In my game engine, I have this (paraphased/simplified here):
window.requestAnimationFrame(this._doFrame);
...
_doFrame: function(timestamp) {
var delta = timestamp - (this._lastTimestamp || timestamp);
for(var i = 0, len = this.elements.length; i < len; ++i) {
this.elements[i].update(delta);
}
this._lastTimestamp = timestamp;
// I used underscore.js's 'bindAll' to make _doFrame always
// get called against my game engine object
window.requestAnimationFrame(this._doFrame);
}
Then each element in my game engine knows how to update themselves. In your case each element that should update every 2, 6, 8 seconds needs to keep track of how much time has passed and update accordingly:
update: function(delta) {
this.elapsed += delta;
// has 8 seconds passed?
if(this.elapsed >= 8000) {
this.elapsed -= 8000; // reset the elapsed counter
this.doMyUpdate(); // whatever it should be
}
}
The Canvas API along with requestAnimationFrame are rather low level, they are the building blocks for things like animation and game engines. If possible I'd try to use an existing one like cocos2d-js or whatever else is out there these days.

Categories

Resources