Does Canvas keep going if you leave the page? - javascript

Click this link, after around 2 seconds you will see some ships coming at you. Now go to a different window for like 15-20 seconds. then come back, you will see a huge wall of enimes coming at you. Now obviously this is because canvas keeps the loops going but is just not letting the came continue on?
So any help would be great, I know this is not an issue during gameplay but if anyone goes off the window then it kind of messes things up...
I tried to solve this by adding these listeners:
window.onblur = function() {
// Add logic to pause the game here...
stopLoop();
};
window.onfocus = function() {
// Add logic to pause the game here...
startLoop();
};
But it does not solve the issue...
The actual loops:
function init()
{
isPlaying = true;
drawBackground();
drawBars();
setUpListeners();
startLoop();
}
and then...
function Loop()
{
if (isPlaying == true)
{
Player1.draw();
requestAnimFrame(Loop);
drawAllEnemies();
}
}
function startLoop()
{
isPlaying = true;
Loop();
startSpawningEnemies();
}
function stopLoop()
{
isPlaying = false;
stopSpawningEnemies();
}
function spawnEnemy(n) //total enemies starts at 0 and every-time you add to array
{
for (var x = 0; x < n; x++)
{
enemies[totalEnemies] = new Enemy();
totalEnemies++;
}
}
function drawAllEnemies()
{
ClearEnemyCanvas();
for(var i = 0; i < enemies.length; i++)
{
enemies[i].draw();
}
}
function startSpawningEnemies()
{
stopSpawningEnemies();
spawnInterval = setInterval(function() {spawnEnemy(spawnAmount);}, spawnRate); //this calls spawnEnemy every spawnRate
/////////spawn 'spawnAmount' enemies every 2 seconds
}
function stopSpawningEnemies()
{
clearInterval(spawnInterval);
}
Actual methods for the enemy:
function Enemy() //Object
{
//////Your ships values
this.EnemyHullMax = 1000;
this.EnemyHull = 1000;
this.EnemyShieldMax = 1000;
this.EnemyShield = 347;
this.SpaceCrystalReward = 2684;
this.EnemySpeed = 2; //should be around 6 pixels every-time draw is called by interval, directly linked to the fps global variable
////////////
////Pick Ship
this.type = "Hover";
this.srcX = EnemySrcXPicker(this.type);
this.srcY = EnemySrcYPicker(this.type);
this.enemyWidth = EnemyWidthPicker(this.type);
this.enemyHeight = EnemyHeightPicker(this.type);
this.drawX = EnemydrawXPicker(this.type);
this.drawY = EnemydrawYPicker(this.type);
////
}
Enemy.prototype.draw = function()
{
this.drawX -= this.EnemySpeed;
ctxEnemy.globalAlpha=1;
ctxEnemy.drawImage(spriteImage,this.srcX,this.srcY,this.enemyWidth,this.enemyHeight,this.drawX,this.drawY,this.enemyWidth,this.enemyHeight);
}
function EnemySrcXPicker(type)
{
if (type == "Hover")
{
return 906;
}
}
function EnemySrcYPicker(type)
{
if (type == "Hover")
{
return 616;
}
}
function EnemydrawXPicker(type)
{
if (type == "Hover")
{
return Math.floor(Math.random() * 1000) + canvasWidthEnemy;
}
}
function EnemydrawYPicker(type)
{
if (type== "Hover")
{
return Math.floor(Math.random() * (canvasHeightEnemy - 72));
}
}
function EnemyWidthPicker(type)
{
if (type == "Hover")
{
return 90;
}
}
function EnemyHeightPicker(type)
{
if (type == "Hover")
{
return 72;
}
}

Depends on the loop.
If you use setTimeOut or setInterval, then yes. The loop will continue even when the window loses focus.
If you use requestFrameAnimation, then no, the loop will stop when the window loses focus.
requestFrameAnimation was created to solve issues like this. Having you PC burn CPU cycles for something not active is just silly.

Related

Dynamically creating oscillators causes memory issues in p5 Sound

Within my code (the code below is a very shortened version), I am creating oscillators (https://p5js.org/reference/#/p5.Oscillator) in draw. Eventually the audio starts crackling and being all weird, and I assume this is because the oscillators aren't being garbage collected? When looking at the Memory tab in Inspect Element, it continously goes up, and the audio goes crackly eventually.
I tried to dereference the oscillators using the two functions in my disconnect variable/function. This sets the oscillator to null after a specific amount of time I hope. I wasn't sure how to set a reference argument to null.
Maybe the p5 sound library still keeps references for them?
function createOsc(grid) {
let osc = new p5.Oscillator();
if (grids[grid].type == 1) {
osc.setType("triangle");
} else if (grids[grid].type == 2) {
osc.setType("sawtooth");
} else if (grids[grid].type == 3) {
osc.setType("square");
}
return osc;
}
function createEnv(grid) {
let env = new p5.Envelope();
env.set(grids[grid].ADSR.attack, grids[grid].vol, grids[grid].ADSR.decay, grids[grid].ADSR.sustain * grids[grid].vol, grids[grid].ADSR.release, 0);
return env;
}
let disconnect = {
list: [],
osc: function(osc, time) {
this.list.push([osc, time, 0]);
},
update: function() {
for (i = 0; i < this.list.length; i++) {
this.list[i][2] += 1 / 60.0;
if (this.list[i][2] >= this.list[i][1]) {
this.list[i][0].a.disconnect();
this.list[i][0].a = null;
this.list.splice(i, 1);
i += -1;
}
}
}
}
function draw() {
let osc = createOsc(0);
let env = createEnv(0);
osc.start();
osc.freq(420);
env.triggerAttack(osc);
env.triggerRelease(osc, env.aTime + env.dTime);
osc.stop(env.aTime + env.dTime + env.rTime);
disconnect.osc({a: osc}, env.aTime + env.dTime + env.rTime);
}
I attempted to use the PolySynth (https://p5js.org/reference/#/p5.PolySynth) object, but I couldn't figure out how to change the waveforms or anything due to lack of clear documentation.
Edit:
This new standalone code produces the same crackling audio after a while. I am also now using Millis() as suggested. Same issue with memory constantly increasing.
let disconnect = {
list: [],
osc: function(osc, time) {
this.list.push([osc, time, millis()]);
},
update: function() {
for (i = 0; i < this.list.length; i++) {
if (millis() - this.list[i][2] >= this.list[i][1]) {
this.list[i][0].a.disconnect();
this.list[i][0].a = null;
this.list.splice(i, 1);
i += -1;
}
}
}
}
function createOsc() {
let osc = new p5.Oscillator();
osc.setType("triangle");
return osc;
}
function createEnv() {
let env = new p5.Envelope();
env.set(0.1, 1, 0.1, 0.8, 0.1, 0);
return env;
}
function setup() {
frameRate(60);
}
function draw() {
disconnect.update();
print(disconnect.list.length);
if (frameCount % 2 == 0) {
let osc = createOsc();
let env = createEnv();
osc.start();
osc.freq(420);
env.triggerAttack(osc);
env.triggerRelease(osc, 0.2);
osc.stop(0.3);
disconnect.osc({a: osc}, 300);
}
}

Can't Implement Javascript Animation Logic

I want to implement some logic in my Javascript animation, but wherever I try to implement it, it never works. I am trying to make my navigationOpen true when the animation is done. But what happens is when I set it in the renderSlideOutAnimation() the if statement runs before the renderSlideOutAnimation function.
function openSlideNavigation() {
var navigationSlideOut = document.getElementById("navigation-slide");
var naviationTrigger = document.getElementById('menu-dots');
var navigationOpen = true;
function renderSlideOutAnimation() {
var widthValue = 0;
var interval = setInterval(frame, 17.5);
function frame() {
if (widthValue == 100) {
clearInterval(interval);
} else {
widthValue++
navigationSlideOut.style.width = widthValue + "vw"
}
}
}
if (navigationOpen == true){
renderSlideOutAnimation();
}
}

Stopping a function from running

I have 3 sequential divs to display on a page, the page loads showing div 1, by going onto the 2nd it starts a timer, when that timer runs out it goes back to the first div. Navigating through to the next div should start the timer again. The timer function works OK on the first page but on the second page when it is called it is already running from the previous div and therefore ticks the time down twice as fast, and on the last div 3 times.
How can I get it to stop the currently running function then restart it?
Thanks,
$scope.timeLeft = 0;
var timeoutRunner = function (timerLength) {
$scope.timeLeft = timerLength;
var run = function () {
if ($scope.timeLeft >= 1) {
console.log($scope.timeLeft);
$scope.timeLeft--
$timeout(run, 1000);
} else if ($scope.timeLeft == 0){
$scope.endTransaction();
}
}
run();
}
timeoutRunner(5);
You need to add some logic that calls $timeout.cancel(timeoutRunner);.
Not sure where you want to cancel your timeout exactly (view change? when you end the transaction?) But here is how you would do it:
$scope.timeLeft = 0;
var timeoutPromise = false;
var timeoutRunner = function (timerLength) {
$scope.timeLeft = timerLength;
var run = function () {
if ($scope.timeLeft >= 1) {
console.log($scope.timeLeft);
$scope.timeLeft--
timeoutPromise = $timeout(run, 1000);
} else if ($scope.timeLeft == 0){
$scope.endTransaction();
$timeout.cancel(timeoutPromise);
}
}
run();
}
timeoutRunner(5);
Each time I called the function it created a new instance of it and I was unable to stop it on demand so I found a way to say which instance I want running:
$scope.timeLeft = 0;
var instanceRunning = 0;
var timeoutRunner = function (timerLength, instance) {
$scope.timeLeft = timerLength;
instanceRunning = instance;
var run = function () {
if (instanceRunning == instance){
if ($scope.timeLeft < 7 && $scope.timeLeft > 0){
$('#timer-container').show();
} else {
$('#timer-container').hide();
}
if ($scope.timeLeft >= 1) {
console.log($scope.timeLeft);
$scope.timeLeft--
$timeout(run, 1000);
} else if ($scope.timeLeft == 0){
$scope.endTransaction();
}
}
}
run();
}
timeoutRunner(20, 1);
timeoutRunner(20, 2);
timeoutRunner(20, 3);

Drawing more than 1 to canvas wont work?

I am trying to draw enemy ships to the canvas that will come from the right at a random x being the height of the canvas and a random y past the right side + 1000.
This works fine however I am trying to make it automated and the code runs run it just does not work on screen, only 1 is drawn? Any more info that you need just ask, It's really frying my brain I went line by line for around 3 hours and can't see an issue.
Before I added this code and just called one manually: http://www.taffatech.com/DarkOrbit.html
After I added this code for automatic: (it kinda looks like its overlapping)
http://www.taffatech.com/test.html
Globals:
var spawnInterval;
var totalEnemies = 0; //leave as is
var enemies = []; //array of enemy objects
var spawnRate = 2000; //every 2 seconds
var spawnAmount = 3; //amount of enemies spawned
Then my init() calls a startLoop:
function startLoop()
{
isPlaying = true;
Loop();
startSpawningEnemies();
}
function stopLoop()
{
isPlaying = false;
stopSpawningEnemies();
}
function Loop()
{
if (isPlaying == true)
{
Player1.draw();
requestAnimFrame(Loop);
drawAllEnemies();
}
then they use these functions:
function spawnEnemy(n) //total enemies starts at 0 and every-time you add to array
{
for (var x = 0; x < n; x++)
{
enemies[totalEnemies] = new Enemy();
totalEnemies++;
}
}
function drawAllEnemies()
{
ClearEnemyCanvas();
for(var i = 0; i < enemies.length; i++)
{
enemies[1].draw();
}
}
function startSpawningEnemies()
{
stopSpawningEnemies();
spawnInterval = setInterval(function() {spawnEnemy(spawnAmount);}, spawnRate); //this calls spawnEnemy every spawnRate
/////////spawn 'spawnAmount' enemies every 2 seconds
}
function stopSpawningEnemies()
{
clearInterval(
spawnInterval);
}
which in turn calls the Enemy class:
function Enemy() //Object
{
//////Your ships values
this.EnemyHullMax = 1000;
this.EnemyHull = 1000;
this.EnemyShieldMax = 1000;
this.EnemyShield = 347;
this.SpaceCrystalReward = 2684;
this.EnemySpeed = 2; //should be around 6 pixels every-time draw is called by interval, directly linked to the fps global variable
////////////
////Pick Ship
this.type = "Hover";
this.srcX = EnemySrcXPicker(this.type);
this.srcY = EnemySrcYPicker(this.type);
this.enemyWidth = EnemyWidthPicker(this.type);
this.enemyHeight = EnemyHeightPicker(this.type);
this.drawX = EnemydrawXPicker(this.type);
this.drawY = EnemydrawYPicker(this.type);
////
}
Enemy.prototype.draw = function()
{
this.drawX -= this.EnemySpeed;
ctxEnemy.globalAlpha=1;
ctxEnemy.drawImage(spriteImage,this.srcX,this.srcY,this.enemyWidth,this.enemyHeight,this.drawX,this.drawY,this.enemyWidth,this.enemyHeight);
}
function EnemySrcXPicker(type)
{
if (type == "Hover")
{
return 906;
}
}
function EnemySrcYPicker(type)
{
if (type == "Hover")
{
return 616;
}
}
function EnemydrawXPicker(type)
{
if (type == "Hover")
{
return Math.floor(Math.random() * 1000) + canvasWidthEnemy;
}
}
function EnemydrawYPicker(type)
{
if (type== "Hover")
{
return Math.floor(Math.random() * (canvasHeightEnemy - 72));
}
}
function EnemyWidthPicker(type)
{
if (type == "Hover")
{
return 90;
}
}
function EnemyHeightPicker(type)
{
if (type == "Hover")
{
return 72;
}
}
for(var i = 0; i < enemies.length; i++)
{
enemies[1].draw();
}
should probably be
for(var i = 0; i < enemies.length; i++)
{
enemies[i].draw();
}
...or you'll draw the same enemy over and over.
In your drawAllEnemies function, shouldn't
enemies[1].draw();
be
enemies[i].draw();
?
When you loop the enemies array you should use the index i that you setup so that it doesn't continually draw the same element:
for(var i = 0; i < enemies.length; i++)
{
enemies[i].draw();
}

countdown timer stops at zero i want it to reset

I am trying to figure out a way to make my countdown timer restart at 25 all over again when it reaches 0. I dont know what I am getting wrong but it wont work.
Javascript
window.onload = function() {
startCountDown(25, 1000, myFunction);
}
function startCountDown(i, p, f) {
var pause = p;
var fn = f;
var countDownObj = document.getElementById("countDown");
countDownObj.count = function(i) {
//write out count
countDownObj.innerHTML = i;
if (i == 0) {
//execute function
fn();
//stop
return;
}
setTimeout(function() {
// repeat
countDownObj.count(i - 1);
},
pause
);
}
//set it going
countDownObj.count(i);
}
function myFunction(){};
</script>
HTML
<div id="countDown"></div>
try this, timer restarts after 0
http://jsfiddle.net/GdkAH/1/
Full code:
window.onload = function() {
startCountDown(25, 1000, myFunction);
}
function startCountDown(i, p, f) {
var pause = p;
var fn = f;
var countDownObj = document.getElementById("countDown");
countDownObj.count = function(i) {
// write out count
countDownObj.innerHTML = i;
if (i == 0) {
// execute function
fn();
startCountDown(25, 1000, myFunction);
// stop
return;
}
setTimeout(function() {
// repeat
countDownObj.count(i - 1);
}, pause);
}
// set it going
countDownObj.count(i);
}
function myFunction(){};
​
I don't see you resetting the counter. When your counter goes down to 0, it executes the function and return. Instead, you want to execute the function -> reset the counter -> return
You can do this by simply adding i = 25 under fn() :
function startCountDown(i, p, f) {
var pause = p;
var fn = f;
var countDownObj = document.getElementById("countDown");
countDownObj.count = function(i) {
// write out count
countDownObj.innerHTML = i;
if (i == 0) {
// execute function
fn();
i = 25;
// stop
return;
}
setTimeout(function() {
// repeat
countDownObj.count(i - 1);
},
pause
);
}
// set it going
in #Muthu Kumaran code is not showing zero after countdown 1 . you can update to this:
if (i < 0) {
// execute function
fn();
startCountDown(10, 1000, myFunction);
// stop
return;
}
The main reason for using setInterval for a timer that runs continuously is to adjust the interval so that it updates as closely as possible to increments of the system clock, usually 1 second but maybe longer. In this case, that doesn't seem to be necessary, so just use setInterval.
Below is a function that doesn't add non–standard properties to the element, it could be called using a function expression from window.onload, so avoid global variables altogether (not that there is much point in that, but some like to minimise them).
var runTimer = (function() {
var element, count = 0;
return function(i, p, f) {
element = document.getElementById('countDown');
setInterval(function() {
element.innerHTML = i - (count % i);
if (count && !(count % i)) {
f();
}
count++;
}, p);
}
}());
function foo() {
console.log('foo');
}
window.onload = function() {
runTimer(25, 1000, foo);
}

Categories

Resources