I am developing a js game and for the update() and draw() method I use requestAnimationFrame. ( I know i should use setInterval for the update). I have a time variable that uses the deltaTime and like that i update the game. When i stop the requsetanimationframe it keeps count again from the stop time. I used the cancelAnimationFrame but it doesnt work. Can someone help me fix the time issue.
let lastTime = 0, reqFrames = 0;
window.restartGame = () => {
window.game.resetAll();
lastTime = 0;
animate(0);
}
function animate(timeStamp){
const deltaTime = timeStamp - lastTime;
lastTime = timeStamp;
ctx.clearRect(0, 0, canvas.width, canvas.height);
window.game.update(deltaTime); // Here is where i update the game.time
window.game.draw(ctx);
if (!game.gameOver) {
window.game.reqFrames = requestAnimationFrame(animate);
}else{
}
}
animate(0);
Related
I want to build a basic function where an Object (Rectangle) is painted, it's x coordinate is changed and than painted again. However, when I execute my code nothing happens until my for loop is over and than everything gets executed at the same time.
ctx = document.querySelector('canvas').getContext('2d');
class Car{
constructor(options){
this.pos = options.pos;
this.size = options.size;
this.color = options.color;
}
draw(){
ctx.fillStyle = this.color;
ctx.fillRect(this.pos[0], this.pos[1], this.size[0], this.size[1]);
}
move(){
this.pos[0] = this.pos[0] + 10;
}
}
const audi = new Car({pos: [10,10], size: [300,20], color: "red"});
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function drawAndMove(){
audi.draw();
audi.move();
sleep(1000);
ctx.clearRect(0, 0, 300, 20);
}
for (let i = 0; i < 10; i++) {
drawAndMove();
}
<canvas></canvas>
When you use sleep(1000) all the script is paused.
Javascript is single threaded language that mean the code is executed synchronously line after line, if you want to make thing append later than they should be, use setTimeout than sleep()
like that :
function drawAndMove(){
audi.draw();
audi.move();
setTimeout(() => {
ctx.clearRect(0, 0, 300, 20);
}, 1000);
}
By this way the code will continue to run sidedly and the script wont be paused !
I change the text color with requestAnimationFrame(animate); function:
requestAnimationFrame(animate);
function animate(time){
... // change text color here
if (offset_s < offset_e) {requestAnimationFrame(animate);}
}
offset_s and offset_s indicates start and end positions of the text for color change. In some cases the animation should last for 2 seconds, but in order cases - for 5 seconds, but offset_e - offset_s could be the same in these two cases. What can I do to control the speed of animation based on given time in seconds/milliseconds?
From the tags of the question i can only see that you animate something drawn on canvas and thats why u cannot use css-animation or jquery-animation.
You have to control the length of the animation by calculating the time difference.
u can do it similar to this example
function start_animate(duration) {
var requestID;
var startTime =null;
var time ;
var animate = function(time) {
time = new Date().getTime(); //millisecond-timstamp
if (startTime === null) {
startTime = time;
}
var progress = time - startTime;
if (progress < duration ) {
if(offset_s < offset_e){
// change text color here
}
requestID= requestAnimationFrame(animate);
}
else{
cancelAnimationFrame(requestID);
}
requestID=requestAnimationFrame(animate);
}
animate();
}
trigger your animation and call start_animate(2000) //duration in millisecond 1000=1 sec
You should separate concerns clearly.
Have a single requestAnimationFrame running, which computes the current animation time and calls every update and draw related functions.
Then your animations would be handled by a function (or class instance if you go OOP) that deals with the current animation time.
Just some direction for the code :
var animationTime = -1;
var _lastAnimationTime = -1;
function launchAnimation() {
requestAnimationFrame(_launchAnimation);
}
function _launchAnimation(time) {
animationTime = 0;
_lastAnimationTime = time;
requestAnimationFrame(animate);
}
function animate(time){
requestAnimationFrame(animate);
var dt = time - _lastAnimationTime ;
_lastAnimationTime = time;
animationTime += dt;
// here call every draw / update functions
// ...
animationHandler.update(animationTime);
animationHandler.draw(context);
}
To start your 'engine', just call launchAnimation then you'll have a valid animationTime and dt to deal with.
I'd make animationHandler an instance of an AnimationHandler class, that allows to add/remove/update/draw animations.
I suggest to use setInterval function in JavaScript.
requestAnimationFrame really needs some 'ugly' calculations. Don't
believe me? Scroll up, you will see...
So, to make setInterval function as handy as rAF(requestAnimationFrame) store the function inside of variable. Here is an example:
var gameLoop = setInterval(function() {
update();
draw();
if (gameOver)
clearInterval(gameLoop);
}, 1000/FPS);
given way, you can control your FPS and pick correct velocity for your objects.
I typically do something like
es6
constructor() {
this.draw();
}
draw() {
const fps30 = 1000 / 30;
const fps60 = 1000 / 60;
window.requestAnimationFrame(() => {
setTimeout(this.draw.bind(this), fps30);
});
}
es5
function DrawingProgram() {
this.draw();
}
DrawingProgram.prototype.draw = function() {
var fps30 = 1000/30;
var fps60 = 1000/60;
var self = this;
window.requestAnimationFrame(function() {
window.setTimeout(function() {
self.draw(); // you could also use apply/call here if you want
}, fps30)
});
}
The canvas animation runs effectively on web browsers but when testing on mobile browsers with iPad and iPhone the animation never starts. It just simply displays the background image. There are no error messages given.
The animation is basically an image that moves from offscreen on the left hand side of the canvas and stops when it reaches 75% of the canvas width.
Heres the code
<script>
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
start();
}
function canvasSupport () {
return Modernizr.canvas;
}
var canvas = document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var image1 = new Image();
image1.onload = function() {
ctx.clearRect(0, 0, 600, 400);
ctx.drawImage(image1, 0, 0);
}
image1.src="images/oven.jpg";
ctx.fillStyle = image1;
var currentX=cw;
var continueAnimating=true;
var nextMoveTime,maxMoves;
var expX = 50;
var expY = 200;
var image2 = new Image();
image2.onload=start;
image2.src="images/pies.png";
var image = new Image();
image.onload=start;
image.src="images/pies.png";
function start(){
maxMoves=(cw+image.width)*0.5;
nextMoveTime=performance.now();
requestAnimationFrame(animate);
function animate(currentTime){
if(continueAnimating){ requestAnimationFrame(animate); }
if(currentTime<nextMoveTime){return;}
nextMoveTime=currentTime; // + delay;
ctx.drawImage(image,currentX,193);
if(--currentX<-image.width){ currentX=cw; }
if(--maxMoves<0){continueAnimating=false;}
}
}
</script>
So the issue comes from your use of performance.now, which is not always implemented, especially on mobile devices where the power drain of a precise timer is too high.
Just use the time provided with the requestAnimationFrame : on accurate browsers/devices, it will use the sub-millisecond accuracy, otherwise it will have only millisecond accuracy.
(accurate = Chrome desktop for sure,... others ???)
I'll let you see below how i use the time of rAF to build the current 'dt' = elapsed time since last frame, and 'applicationTime' = time elapsed in the application (not counting when you tabbed out the app).
A secondary benefit of this method is that you can change easily the application speed to have 'bullet-time' or speed up (or even rewind if speed is <0).
fiddle is here :
http://jsfiddle.net/gamealchemist/KVDsc/
// current application time, in milliseconds.
var applicationTime = 0;
// scale applied to time.
// 1 means no scale, <1 is slower, >1 faster.
var timeSpeed = 1;
// after launchAnimation is called,
// draw/handleInput/update will get called on each rAF
function launchAnimation() {
requestAnimationFrame(_launchAnimation);
}
// ------------- Private methods ----------------
function _launchAnimation(now) {
_lastTime = now;
applicationTime = 0
requestAnimationFrame(_animate);
}
// ----------------------------------------------
// Animation.
// Use launchAnimate() to start the animation.
// draw, handleInput, update will be called every frame.
// ----------------------------------------------
function _animate(now) {
requestAnimationFrame(_animate);
// _______________________
var dt = now - _lastTime;
if (dt < 12) return; // 60 HZ max
if (dt > 200) dt = 16; // consider 1 frame elapse on tab-out
_lastTime = now;
dt *= timeSpeed;
applicationTime += dt;
// _______________________
handleInput(); // ...
// update everything with this frame time step.
update(dt);
// draw everything
draw();
}
var _lastTime = 0;
(( Notice that to handle most gracefully the tab-out, you have to handle the blur event, cancel the rAF, then set-it again on focus. ))
var now =
( typeof performance === 'object' && 'now' in performance )
? function() { return performance.now(); }
: function() { return ( new Date ).getTime(); };
here is the code:
var ctx = canvas.getContext('2d');
var chop1 = new Image();
chop1.src = "img/chopper.png";
var blt = new Image();
blt.src = "img/bullet.png"
var chopperX = 0;
var chopperY = 0;
var ascent = 2;
var limit = 500;
var start = null;
var bltX = 135;
function fire()
{
bltX +=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(blt,bltX,20 ,
chop1.width, chop1.height);
requestAnimationFrame(fire);
}
function up(){
chopperY-=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
requestAnimationFrame(up);
if(chopperY == 20){
setInterval(fire,1000)
}
if(chopperY == 0){
fly();
}
}
function fly() {
chopperY+=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
if (chopperY < limit) {
requestAnimationFrame(fly);
}
if(chopperY==limit){
up();
}
fly();
As per the condition(chopperY == 20),"fire" method is excute.But i want to call "fire" and "fly" function simultaneously at this particular condition. Is there any way to do this? In this code when "fire" method excute then "fly" method stop automatically.Is there any way to solve this issue?
It looks like you are making a game. You should use event listeners. See some examples here or keyboard examples. This would allow you to connect keyboard actions to code and they should run at the same time.
I'm not sure if I understand you correctly but if your goal is, to pass two functions to setInterval, than you can achieve this by wrapping them up in an anonymous function:
if(chopperY == 20){
setInterval(function () {
fire();
fly();
},1000)
}
This will call both functions every second. However, the fire function will get executed first.
I am implementing a simple animation using javascript in canvas.An image updates its position based on the time elapseddt between each frame. Here is the code
var lastTime=0;
var speed=100;
mySprite = function() {
this.pos=[0,0];
}
function spritePosition(dt) {
for (i=0; i < Stuff.sprite.length;i++) {
Stuff.sprite[i].pos[0] += speed*dt;
}
}
function animate(){
var canvas=document.getElementById('mycan');
var context=canvas.getContext('2d');
var now = Date.now();
var dt = (now - lastTime) / 1000.0;
//clear
context.clearRect(0, 0, canvas.width, canvas.height);
//update
spritePosition(dt);
updateSprite();
//render
background(canvas,context);
draw(context);
lastTime = now;
//request new Frame
requestAnimFrame(function() {
animate();
});
}
window.onload=function(){
init();
animate();
}
dt
values are in the range 0.3-0.5
But the line
Stuff.sprite[i].pos[0] += speed*dt;##
assigns position values as 136849325664.90016.
Please help.
You initialize lastTime to 0 - so the initial delta is veeeeery long (as of today almost 45 years!). You should make sure to catch the very first run (compare to 0? or initialize lastTime with Date.now()) and treat it separately, possibly setting dt to 0.