Fill canvas before alert - javascript

For a canvas game, how can I load a canvas fill and text before the window alert? Even by just a few milliseconds.
There is a collision that occurs between two players.
Immediately after, the canvas should fill as colour and text should be displayed.
The problem is the alert appears before this happens.
The alert, when OK pressed, should reload the page - I have found that setTimeout does not work because of the location.reload inside of it.
Quick (nasty) example of how it works currently: JSFiddle
//collision
if (x < object.x - 50 + 60 &&
x + width > object.x - 50 &&
y < object.y - 60 + 60 &&
height + y > object.y - 60) {
//fill before alert
ctx.fillRect(0,0,2000,2000);
ctx.strokeText("You only reached a score of " + score + ", you lose!\nPress 'OK' to try again?", 250, 290);
ctx.fillText("You only reached a score of " + score + ", you lose!\nPress 'OK' to try again?",250,290);
//end game alert
if(!alert("You reached a score of ...")){
location.reload();
}}

setTimeout perfectly works, check this code out
HTML
<canvas id="canvas"></canvas>
<button id="buttonID">click here</button>
Javascript
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
ctx.fillStyle=gradient;
var my_gradient=ctx.createLinearGradient(0,0,0,170);
my_gradient.addColorStop(0,"#7E6189");
my_gradient.addColorStop(1,"#FFFFFF");
ctx.fillStyle=my_gradient;
ctx.lineWidth = 5;
ctx.font="30px Verdana";
var gradient=ctx.createLinearGradient(0,0,canvas.width,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","red");
ctx.strokeStyle = 'black';
//irrelevant above
var x = 1 //collision or any true statement
// when the event occures
// create the gredient
document.querySelector('#buttonID').onclick = function() {
ctx.fillRect(0,0,2000,2000);
ctx.strokeText("You only reached a score of ...", 250, 290);
ctx.fillText("You only reached a score of ...",250,290);
setTimeout(function() {
if(!alert("You only reached a score of ...")){
location.reload();
}
}, 300); // chenge the millis to whatever you want
}
i am using an event to execute the code because the permanent alets are annoying

This should work.
var x = 1 //collision or any true statement
if (x == 1) {
ctx.fillRect(0, 0, 2000, 2000);
ctx.strokeText("You only reached a score of ...", 250, 290);
ctx.fillText("You only reached a score of ...", 250, 290);
setTimeout(function(){
alert("You only reached a score of ...");
location.reload();
}, 10);
}
https://jsfiddle.net/ajrwpmb3/10/
Alerts does not return any value. they block the complete code execution. The problem here was that browsers tend to do bulk updates to DOM for changes done inside a function. Since in your code changes to canvas as done and immediately browser thread is blocked by alert, therefore the changes are not applied. in my example i have added 10 ms to break the event of canvas update and alert into 2 blocks in browser's event queue, this allows browser to complete event 1 i.e. canvas update first and then execute 2nd event i.e. show alert which blocks the code.
You can validate this concept by using the timeout value as 0ms.

window.alert, confirm, and prompt should die..
So many answers but I will add one more.
DON'T USE ALERTS... ever... please!!!, for the sake of all the users of your site and because if they are like me the very first time I see an alert on a site I turn them off. The fact that users can block/stop these alerts should be more than reason enough to not use them, because relying on alerts will break your game for pedantic people like me.
A much better Alert
Your best option is to create your own non blocking, customized alert. Create a div set its style to position: absolute, center it to the page, then add an ok button and hide the whole thing.
Give the alert div the id="myAlert" and when you need it just unhide it and wait for the button to be clicked, when the button is clicked hide the alert.
For your game you should add some pause logic, and when the alert needs to be shown just pause the game and show the alert. When the alert has been clicked unpause the games and do what is needed.
The demo just shows how you can implement a simple custom alert and pause a game.
// alert function handles the display of a custom alert
var alert = (function(){
var firstRun = true;
var callback;
function alertCallback(){ // handle the click event
if(typeof callback === "function"){
callback();
}
myAlert.className = "alert hideAlert";
}
function alert(message,callbackO){
myAletMessage.textContent = message;
myAlert.className = "alert showAlert";
if(firstRun){
firstRun = false;
myAlertButton.addEventListener("click",alertCallback)
}
callback = callbackO; // set the callback
}
return alert;
})()
var canvas,ctx;
function addCanvas(){
canvas = document.createElement("canvas");
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.position = "absolute";
document.body.appendChild(canvas);
ctx = canvas.getContext("2d");
}
function resizeCanvas(){
if(canvas === undefined){
addCanvas();
}
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.font = "32px arial black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "red";
}
resizeCanvas();
window.addEventListener("resize",resizeCanvas);
// The following code just shows a pretend game with a paused function
// The paused is used when the alert is called.
var paused = false;
var x,y,t;
function update2(timer){
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.globalAlpha = 1; // reset alpha
if(paused){
if(t > 0){
var sc = t / 10 + 0.1;
ctx.setTransform(sc,0,0,sc,x,y);
ctx.fillText("BANG!",0,0);
}
var sc = Math.sin(timer /100) * 0.1 + 1.0;
ctx.fillStyle = "Green";
ctx.setTransform(sc,0,0,sc,canvas.width/2,canvas.height/2);
ctx.fillText("Paused!",0,0);
ctx.fillStyle = "Red";
}else{
if(t > 0){
var sc = t / 10 + 0.1;
ctx.setTransform(sc,0,0,sc,x,y);
ctx.fillText("BANG!",0,0);
t -= 1;
}else{
if(Math.random() < 0.1){
x = (Math.random() * (canvas.width -200))+100;
y = (Math.random() * (canvas.height -200))+100;
t= 10;
}
}
if(Math.random() < 0.01){
alert("A 1 in 100 random event paused the game!",function(){paused = false;});
paused = true;
}
}
requestAnimationFrame(update2);
}
requestAnimationFrame(update2);
.alert {
position : absolute;
z-index : 100;
left : 35%;
width : 30%;
top : 10%;
background: #fff;
border: 1px #000 solid;
text-align: center;
padding: 6px;
}
.hideAlert {
display : none;
}
.showAlert {
display : block;
}
<div class="alert hideAlert" id="myAlert">
<div id="myAletMessage"></div><br>
<input id="myAlertButton" type="button" value="OK got it."></input>
</div>

Related

Javascript: How can i display a pop up box in the canvas when timer ends?

i am very new to javascript and i have been stuck with this issue for a while, where i want to display a pop up box with a message "Game Over, you ran out of time" so the player knows the game is finished.The game is simple, when the game starts a countdown will commence and the user has to answer as many questions correctly within the time limit, for each correct answer, a red ball will travel upwards and the score will increment.The seconds and run-timer function, is in example i modified it a bit, but now i am lost as i really don't know to go about it, i want to modify it further so when the timer reaches 0:00 a window should pop up.Here is my full code so far.
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
let x = 450,
y = 350;
let count = 0;
window.onload = function() {
let btn = document.getElementById("submitAnswers");
btn.onclick = jump;
};
document.getElementById('timer').innerHTML = 01 + ":" + 11;
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {
sec = "0" + sec
};
if (sec < 0) {
sec = "59"
};
return sec;
}
function runTimer() {
const presentTime = document.getElementById('timer').innerHTML;
const timeArray = presentTime.split(/[:]+/);
let m = timeArray[0],
s = checkSecond((timeArray[1] - 1));
if (s == 59) {
m -= 1
}
if (m < 0) {
return
}
document.getElementById('timer').innerHTML = m + ":" + s;
}
setInterval(runTimer, 1000);
function submitAnswers() {
const answer1 = document.querySelector("#Q1").value
if (answer1 == 40) jump();
const answer2 = document.querySelector("#Q2").value
if (answer2 == 25) jump();
}
function jump() {
count += 1;
//changing the y position
y -= 6;
}
function draw() {
//clearing the canvas
context.clearRect(0, 0, 600, 400);
//redrawing the circle
context.beginPath();
context.arc(x, y, 50, 0, 2 * Math.PI);
context.fillStyle = "red";
context.fill();
//drawing the count value
context.font = '25px Arial';
context.fillStyle = 'white';
context.fillText("Count: " + count, 20, 30);
context.font = '25px Arial';
context.fillStyle = 'white';
let theTime = document.getElementById('timer').textContent;
context.fillText("Timer: " + theTime, 200, 30);
window.requestAnimationFrame(draw);
}
draw();
canvas {
width: 100%;
background-color: black;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<h1 align="center"></h1>my jumper game </h1>
<p align="center"> </p>A Fun flappy-bird like game</p>
<div>
<p>What is 10 times 4 ? <input type="number" id="Q1"></p>
<p>What is 5 * 5 ? <input type="number" id="Q2"></p>
<p>What is 5 * 3 ? <input type="number" id="Q3"></p>
<button onclick="submitAnswers()" id="submitAnswers">Submit</button>
</div>
<canvas id="canvas" width="900" height="400">
Your browser does not support the HTML5 canvas tag.
</canvas>
<br> </br>
<body onload="startGame()">
<div>Time left = <span id="timer"></span>
</body>
</html>
What you would need to do is to create a "popup" within the canvas itself.
You can create an object with a white background color, then add a border and text within it. You'd probably also need to add a button to close the "popup".
Use resources like the below articles to help figure it out, but I'm pretty sure you can use HTML elements within the canvas tag to help create this modal box. It looks like you already know how to deal with text, so this shouldn't be difficult for you.
https://www.tutorialspoint.com/html5/html5_canvas.htm
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
BTW, canvas objects are just like any other object in that you can keep track of them. With canvas, if you delete an object is covering another object, it reveals the covered object. This is the difference between raster and vector, where raster would replace the visual data/elements and vector layers the visual information.
If you want to keep it very simple you could go with the "alert" method. Place it inside the runTimer function:
if (m < 0) {
alert("You ran out of time!");
return;
}
I don't know if it's possible to add a custom popup from within the canvas - maybe this would be a little overkill for a simple game?

requestAnimationFrame loop not correct FPS

I have a javascript function that my game loops through (hopefully) 60 times a second that controls input, drawing, etc.
The way it is currently coded it seems to be always be around 52, noticeably lower than 60 fps, and it even dips to 25-30 fps even when nothing else is happening
function loop() {
setTimeout(function () {
requestAnimationFrame(loop);
time += (1000 / 60);
if (time % 600 == 0) {
oldtick = tick;
tick += 1;
time = 0;
aiMovement();
combat();
}
context.clearRect(0, 0, c.width, c.height);
drawMap();
playerInput();
movePlayer();
drawEntities();
drawPopups();
var thisLoop = new Date;
var fps = 1000 / (thisLoop - lastLoop);
lastLoop = thisLoop;
context.drawImage(cursor, mouse.x, mouse.y, 16, 16);
context.fillStyle = "#ffff00";
context.fillText("FPS: " + Math.floor(fps) + " Time: " + Math.floor(time) + " tick: " + tick, 10, 450);
context.fillText("Gold: " + gold, 10, 460);
//requestAnimationFrame(loop);
}, 1000 / 60);
}
if I remove the setTimeout and the first requestAnimationFrame from the top and uncomment the reuqestAnimationFrame at the bottom and remove the other setTimeout things, the FPS improves to 58 but rapidly changes between 58 and 62, again, not statically 60. Does it have something to do with 1000/60 is not a whole number? How would people using requestAnimationFrame achieve 60 fps if this was true?
Don`t use setTimeout or setInterval for animation.
The problem is that you are calling a timer event from within the request animation event. Remove the timeout and just use requestAnimationFrame.
function loop(time){ // microsecond timer 1/1,000,000 accuracy in ms 1/1000th
// render code here
requestAnimationFrame(loop);
// or render code here makes no diff
}
requestAnimationFrame(loop); // to start
RequestAnimationFrame (rAF) is always in sync (unless the browser has vertical sync turned off). The next frame will be presented in 1/60th, 2/60th, 3/60th etc... of a second. You will not get 52frame per second using rAF, rather 60fps, 30fps, 15fps, etc...
The Demo below show the difference in use.
Because requestAnimationFrame uses some smarts to time the animation they can not both run at the same time so click on the canvas to start it.
You can also add a load to simulate rendering. There is a 14ms load and a 28 ms load. The 28ms load is design to mess up rAF as it will on many machines flick between 30 and 60 frames per second. The point is to show that rAF can only have 60, 30, 20,.. etc frames per second.
var ctx1 = can1.getContext("2d");
var ctx2 = can2.getContext("2d");
var ctx3 = can3.getContext("2d");
var lastTime1 = 0;
var lastTime2 = 0;
var lastTime3 = 0;
var frameFunction = frame1;
var frameText = "";
var drag = false;
var loadAmount = 14;
var stats = [{
data : [],
pos : 0,
add(val){
this.data[(this.pos ++) % 150] = val;
}
},{
data : [],
pos : 0,
add(val){
this.data[(this.pos ++) % 150] = val;
}
},{
data : [],
pos : 0,
add(val){
this.data[(this.pos ++) % 150] = val;
}
}
];
for(let i = 0; i < 150; i += 1){
stats[0].add(0);
stats[1].add(0);
stats[2].add(0);
}
setupContext(ctx1);
setupContext(ctx2);
setupContext(ctx3);
drawFrameTime(ctx1,0);
drawFrameTime(ctx2,0);
drawFrameTime(ctx3,0);
can1.addEventListener("click",()=>frameFunction = frame1);
can2.addEventListener("click",()=>frameFunction = frame2);
can3.addEventListener("click",()=>frameFunction = frame3);
load.addEventListener("click",()=>{
if(drag){
drag = false;
load.value = "Add load.";
}else{
drag = true;
load.value = "Remove load.";
}
});
loadPlus.addEventListener("click",()=>{
if(loadAmount === 14){
loadAmount = 28;
loadPlus.value = "28ms";
}else{
loadAmount = 14;
loadPlus.value = "14ms";
}
});
function CPULoad(){
if(drag){
var stopAt = performance.now() + loadAmount;
while(performance.now() < stopAt);
}
}
function setupContext(ctx){
ctx.font = "64px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
}
function drawStats(ctx,stat){
ctx.setTransform(1,0,0,1,0,64);
ctx.strokeStyle = "red";
ctx.strokeRect(-1,16.666,152,0);
ctx.strokeStyle = "black";
ctx.beginPath();
var i = stat.pos + 149;
var x = 0;
ctx.moveTo(x,stat.data[(i++) % 150]);
while(x ++ < 150 && stat.data[i % 150] !== undefined) {
ctx.lineTo(x,stat.data[(i++) % 150]);
}
ctx.stroke();
}
function drawFrameTime(ctx,time){
ctx.fillStyle = "black";
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
if(time > 0){
ctx.fillStyle = drag ? "red" : "black";
ctx.setTransform(1,0,0,1,ctx.canvas.width / 2,ctx.canvas.height *0.25);
ctx.fillText(time,0,0);
ctx.setTransform(0.4,0,0,0.4,ctx.canvas.width / 2,ctx.canvas.height * 0.75);
ctx.fillText(Math.round(1000 / Number(time)) + "fps",0,0);
}else{
ctx.setTransform(0.4,0,0,0.4,ctx.canvas.width / 2,ctx.canvas.height * 0.75);
ctx.fillText("Click to Start.",0,0);
}
ctx.fillStyle = "black";
ctx.setTransform(0.2,0,0,0.2,ctx.canvas.width / 2,ctx.canvas.height * 0.9);
ctx.fillText(frameText,0,0);
if(drag){
ctx.fillStyle = "red";
ctx.setTransform(0.2,0,0,0.2,ctx.canvas.width / 2,ctx.canvas.height * 0.5);
ctx.fillText("Load " + loadAmount + "ms",0,0);
}
}
function frame1(time){
requestAnimationFrame(frameFunction);
frameText = "Using rAF.";
var frameTime = time - lastTime1;
lastTime1 = time;
stats[0].add(frameTime);
drawFrameTime(ctx1,frameTime.toFixed(2));
drawStats(ctx1,stats[0]);
CPULoad()
}
function frame2() {
setTimeout(function () {
frameText = "Using rAF & setTimeout.";
var time = performance.now();
var frameTime = time - lastTime2;
stats[1].add(frameTime);
lastTime2 = time;
drawFrameTime(ctx2, frameTime.toFixed(2));
drawStats(ctx2,stats[1]);
CPULoad();
requestAnimationFrame(frameFunction);
}, 1000 / 60);
}
function frame3() {
setTimeout(frameFunction,1000/60);
frameText = "SetTimeout by itself.";
var time = performance.now();
var frameTime = time - lastTime3;
stats[2].add(frameTime);
lastTime3 = time;
drawFrameTime(ctx3, frameTime.toFixed(2));
drawStats(ctx3,stats[2]);
CPULoad();
}
requestAnimationFrame(frameFunction);
body {
font-family : arial ;
}
canvas {
border : 1px solid black;
}
div {
text-align : center;
}
<div><h2>RequestAnimationFrame (rAF)</h2>
rAF V rAF & setTimeout V setTimeout<br>
<canvas id = can1 width = 150></canvas>
<canvas id = can2 width = 150></canvas>
<canvas id = can3 width = 150></canvas><br>
Click the frame to set the current test.<br>
The left frame is using rAF alone, the middle using setTimeout and rAf, and the rigth frame uses setTimeout alone.<br>
Click <input type="button" id=load value="add Load"></input> to simulate a rendering load of around <input type="button" id=loadPlus value="14ms" title="click to change CPU load between 14 and 28ms"></input> <br>
Try draging and selecting this text and see how it effects the different methods.<br>
rAF is by far the most stable of the 3.<br>
</div>
The functions purpose isnt to have 60 FPS, but to draw while a frame is beeing drawn and make the performance better. No computer will stay perfectly at 60 FPS.
Also, why is your requestAnimationFrame IN the timeout?

Playing with Intervals in javascript

I have my object called player
var player = new Object();
player = {
x: (window.innerWidth / 2),
y: (window.innerHeight / 2),
width: 30,
height: 30,
color: 'red'
};
And some variable to check mouse down clicks
var mouseDown = 0;
And I have functions that is firing every 40ms...
function drawPlayer() {
var toPlayerX;
var toPlayerY;
var toPlayerLength;
toPlayerX = player.x - mouseX;
toPlayerY = player.y - mouseY;
toPlayerLength = Math.sqrt(toPlayerX * toPlayerX + toPlayerY * toPlayerY);
toPlayerX = toPlayerX / toPlayerLength;
toPlayerY = toPlayerY / toPlayerLength;
toPlayerLength = toPlayerLength - (toPlayerLength%1);
// this function get reduced distance between mouse and player canvas rect by 1px per click
function movePlayer() {
player.x -= toPlayerX;
player.y -= toPlayerY;
}
// on MOUSE DAWN EVENT
document.body.onmousedown = function() {
// on every mouse down click ++ mousedawn
++mouseDown;
// here we fire interval to make player alive and
// follow to mouse dawn click by ~25 pixels per secord
if (mouseDown==1) {
setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(movePlayer());
mouseDown = 0;
}
};
}
The root of the problem is in this part
if (mouseDown>1) {
clearInterval(movePlayer());
mouseDown = 0;
}
I thought it will be clear movePlayer interval if mouseDown will have number of two, but instead the setInterval(movePlayer) just multiplie all the time while I click mouse, and making mouseDown = 0 works pretty well when it's go number of 2.
At the beginning I just wanna make code so when user clicks in some area the canvas player would follow straightly on area where the mouse was clicked and then stopped and while for example player is going somewhere already and the mouse was click again to change canvas first destination to area where the mouse was clicked last.
You have to set the setInterval command to a variable, and that is what you use to clear it.
So your code would look something like this:
// this function get reduced distance between mouse and player canvas rect by 1px per click
function movePlayer() {
player.x -= toPlayerX;
player.y -= toPlayerY;
}
var intervalID; // Declare the variable that is storing the interval.
// on MOUSE DAWN EVENT
document.body.onmousedown = function() {
// on every mouse down click ++ mousedawn
++mouseDown;
context.save();
context.fillStyle = 'black';
context.restore();
// here we fire interval to make player alive and follow to mouse dawn click by ~15 pixels per secord
if (mouseDown==1) {
intervalID = setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(intervalID);
mouseDown = 0;
}
};
document.body.onmouseup = function() {
//--mouseDown;
};
clearInterval takes an intervalID returned from setInterval.
var timeoutId = 0;
if (mouseDown==1) {
timeoutId = setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(timeoutId);
mouseDown = 0;
}
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval

Prevent requestAnimationFrame from running all the time

I'd like to know how to call the animate function through requestAnimationFrame only when it's realy needed. Currently the animate is called all the time what generates an overhead I guess.
I already tried inside my animate function to compare targetRadius and the inital radius and return false once they are the same. Unfortunately this doesn't work at all.
Can someone explain me how to solve that?
jsfiddle
HTML:
<canvas id="ddayCanvas" width="288" height="288" data-image="http://www.topdesignmag.com/wp-content/uploads/2011/07/64.png">
<div>
<div class="product-image"></div>
<div class="product-box">...</div>
...
</div>
</canvas>
JS:
// Options
var maxImageWidth = 250,
maxImageHeight = 196;
var canvas = $('#ddayCanvas'),
canvasWidth = canvas.width(),
canvasHeight = canvas.height(),
sectorColor = $('.product-box').css('background-color'),
context = canvas[0].getContext('2d'),
imageSrc = canvas.data('image'),
imageObj = new Image(),
imageWidth, imageHeight,
mouseover = false;
imageObj.onload = function() {
imageWidth = this.width;
imageHeight = this.height;
if (imageWidth > maxImageWidth){
imageHeight = imageHeight - (imageWidth - maxImageWidth);
imageWidth = maxImageWidth;
}
if (imageHeight > maxImageHeight) {
imageWidth = imageWidth - (imageHeight - maxImageHeight);
imageHeight = maxImageHeight;
}
drawDday(90);
};
imageObj.src = imageSrc;
function drawDday (radius) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight);
context.fillStyle = sectorColor;
context.beginPath();
context.rect(0, 0, canvasWidth, canvasHeight);
context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
// Check out the console
console.log('test');
}
var radius = baseRadius = 90,
targetRadius = 110,
ease = 50,
speed = 2;
function animate(){
if(mouseover){
radius += ((targetRadius-radius)/ease)*speed;
} else {
radius -= ((radius-baseRadius)/ease)*speed;
}
if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;
drawDday(radius);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
canvas.on('mouseover', function(e){
mouseover = true;
}).on('mouseout', function(){
mouseover = false;
});
You need to implement a condition so you can break the loop, for example (adopt as needed):
var isRunning = true;
function loop() {
... funky stuff here ...
/// test condition before looping
if (isRunning) requestAnimationFrame(loop);
}
Now when you set isRunning to false the loop will break. For convenience it's recommended that you have a method to start and stop the loop:
function startLoop(state) {
if (state && !isRunning) {
isRunning = true;
loop(); /// starts loop
} else if (!state && isRunning) {
isRunning = false;
}
}
The condition can be set by anything you need it to be set by, for example on a callback after an animation has finished etc. The important part is that the condition flag is available to both scopes using it (ie. most commonly in the global scope).
UPDATE:
More specific in this case is that your condition (radius) will never reach the condition required to eventually stop the loop.
Here is what you can do to fix this:
DEMO
var isPlaying = false;
function animate(){
/**
* To make sure you will reach the condition required you need
* to either make sure you have a fall-out for the steps or the
* step will become 0 not adding/subtracting anything so your
* checks below won't trigger. Here we can use a simple max of
* the step and a static value to make sure the value is always > 0
*/
if(mouseover){
radius += Math.max( ((targetRadius-radius)/ease)*speed, 0.5);
} else {
radius -= Math.max( ((radius-baseRadius)/ease)*speed, 0.5);
}
/**
* Now the checks will trigger properly and we can use the
* isPlaying flag to stop the loop when targets are reached.
*/
if(radius >= targetRadius) {
radius = targetRadius;
isPlaying = false; /// stop loop after this
} else if (radius <= baseRadius) {
radius = baseRadius;
isPlaying = false; /// stop loop after this
}
drawDday(radius);
/// loop?
if (isPlaying === true) requestAnimationFrame(animate);
}
In order to trigger the loop we use a method that will check if the loop is running, if not it will reset the isPlaying flag and start the loop. We do this inside both mouseover and mouseout:
canvas.on('mouseover', function(e){
mouseover = true;
startAnim();
}).on('mouseout', function(){
mouseover = false;
startAnim();
});
The method is simply checking isPlaying and if not set it set it to true and starts the loop - this so that the loop is only started once:
function startAnim() {
if (!isPlaying) {
isPlaying = true;
requestAnimationFrame(animate);
}
}
In the demo I added console logging to show when the loop is running and when targets are hit.
Hope this helps.
The reason your animate function is being called continuously is because you start off by calling requestAnimationFrame(animate); and then each call to animate unconditionally calls requestAnimationFrame(animate); again. The cycle is never going to be broken unless you use cancelAnimationFrame at some point (which you don't), or make sure that animate only requests another frame if it's needed.
Another issue is the fact that radius will currently never reach either targetRadius nor baseRadius, and therefore neither of the following will ever be true:
if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;
This isn't directly responsible for the continual calls to animate, but since targetRadius and baseRadius are being used to indicate the end-points of your animation then we need to form some sort of sensible conditional with them.
So, you could do something like: http://jsfiddle.net/PLDUq/9/
var radius = baseRadius = 50,
targetRadius = 110,
ease = 50,
speed = 12,
currentAnim;
function animate(){
if(mouseover){
radius += ((targetRadius-radius)/ease)*speed;
} else {
radius -= ((radius-baseRadius)/ease)*speed;
}
drawDday(radius);
if(Math.round(radius) >= targetRadius) {
// uses Math.round() to ensure condition can be fulfilled
radius = targetRadius;
return; // doesn't call next frame
}
if(Math.round(radius) <= baseRadius) {
radius = baseRadius;
return; // doesn't call next frame
}
requestAnimationFrame(animate);
}
canvas.on('mouseenter mouseleave', function (e) {
if (currentAnim) {requestAnimationFrame(currentAnim);}
// cancels current animation if one is playing to
// prevent several concurrent loops calling animate()
mouseover = (e.type === 'mouseenter');
requestAnimationFrame(animate);
});

Countdown timer not working

The countdown timer I created doesn't work.
Interestingly, if I use console.log to print the value of count--which starts at 3--something like -3498 is printed, even if I am only on for around 15 seconds, so there must be something wrong with the set interval code. The value is displayed(if count is greater than 0), but set interval changes too quickly.
Here's the code.
function countdown(){
window_width=window.innerWidth-70;
window_height=window.innerHeight-150;
canvas = document.getElementById("gameCanvas");
ctx=canvas.getContext("2d");
canvas.width = window_width;
canvas.height=window_height;
if(count>0){
ctx.font = '40pt Calibri';
ctx.fillStyle = "white";
ctx.fillText(count, window_width/3, window_height/2);
}
else if(count===0){
ctx.fillText("Go!", window_width/3, window_height/2);
}
else{
return;
}
setInterval(function(){count=count-1},1000);
requestAnimationFrame(countdown);
}
Any help would be appreciated.
I'm a bit unclear as to what you're trying to accomplish, but here's a shot at it:
window.count = 3;
setTimeout(countdown,1000);
function countdown(){
window_width=window.innerWidth-70;
window_height=window.innerHeight-150;
canvas = document.getElementById("gameCanvas");
ctx=canvas.getContext("2d");
canvas.width = window_width;
canvas.height=window_height;
count--;
if(count>0){
ctx.font = '40pt Calibri';
ctx.fillStyle = "white";
ctx.fillText(count, window_width/3, window_height/2);
setTimeout(countdown,1000);
}
else if(count===0){
ctx.fillText("Go!", window_width/3, window_height/2);
}
else{
return;
}
requestAnimationFrame(countdown); // not sure if this is needed
}
As far as I can tell, you don't need the interval.
The value is displayed, but changes too quickly
You will need to distinguish between logic and presentation. When the countdown function is called, you schedule a function to decrease the count in 1s, and at the same time you schedule countdown to be called again as fast as possible. The update interval of canvases is about 16ms though… It means the count is decreased at that rate, only delayed 1 second after starting.
And it's even worse, as you did use setInterval which forever repeats decreasing the count, while that process is started every animation frame…
The solution is not to use setTimeout/setInterval at all. They are unreliable and should not be used for measuring the time. Instead, get accurate timestamps from the Date object (every time you need them, ie. every animation frame):
var canvas = document.getElementById("gameCanvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth -70;
canvas.height = window.innerHeight-150;
ctx.font = '40pt Calibri';
ctx.fillStyle = "white";
var count = …;
var estimatedEnd = Date.now() + count*1000;
function countdown(){
var currentCount = (estimatedEnd - Date.now()) / 1000;
if (currentCount>0){
ctx.fillText(currentCount, window_width/3, window_height/2);
requestAnimationFrame(countdown);
} else if (currentCount===0){
ctx.fillText("Go!", window_width/3, window_height/2);
}
}
countdown();

Categories

Resources