Generating enemies while countdown is running - javascript

I'm currently trying to create a game during a gamejam using HTML CSS Javascript and jQuery.
I have a countdown that shows to the player how many time he has left, and that loses 2 seconds when the main character is getting hit by the enemies.
What I'd like to do is to find a way to generate enemies every x seconds as long as the timer hasn't reached 30s (where he wins) or 0s (where he loses)
I used two libraries so far, QuintusJS (link here) for the physic and jchavannes's countdown (link here), trying to make them work together.
So far, everything has been great, but I can't manage to make a jQuery loop that would look like the following,
while(currentTime < 30000){ //30s in milliseconds
stage.insert(new Q.Enemy({ x: 700, y: 0 })); //Allows ennemies to be generated by QuintusJS
//TODO : adding a delay between enemies generation
}
Because the canvas that Quintus creates becomes totally blank. I also tried to create a function that sets checkCurrentTime to true while currentTime isn't equal to 0, but changes it to false when it's the case, but it still doesn't work.
Am I doing this wrong? If so, how can I manage to have a loop that generates enemies properly?

You can create a looper that does something every N milliseconds. For example:
var looper = function (delay, callImmediately, callback) {
var self = this;
var deferred = $.Deferred();
if (callImmediately) {
callback.call(self);
}
var iv = setInterval(function () {
if (deferred.state() === "resolved" || deferred.state() === "rejected") {
clearInterval(iv);
} else {
callback.call(self);
}
}, delay);
return deferred;
};
You can then start and stop it whenever you like:
// Every second add a new enemy
var loop = looper(1000, false, function () {
console.log("Adding an enemy");
//stage.insert(new Q.Enemy({ x: 700, y: 0 }));
});
// Handle done and fail
loop.done(function () { console.log("All enemies were added!"); });
loop.fail(function () { console.log("The loop was stopped before all enemies were added!"); });
// Resolve the looper after 30 seconds (calls .done())
setTimeout(loop.resolve, 30000);
// Or if something went wrong you can also reject it (calls .fail())...
setTimeout(loop.reject, 5000);
Here is a small example:
var player = {
health: 100,
};
// Damage the player every second
var iv = setInterval(function () {
player.health -= Math.floor(Math.random() * 10);
if (player.health <= 0) {
clearInterval(iv);
}
}, 1000);
// Every second add a new enemy
var loop = looper(1000, false, function () {
if (player.health <= 0) {
loop.reject("The player is dead. Stopping.");
} else {
//stage.insert(new Q.Enemy({ x: 700, y: 0 }));
console.log("Inserting an enemy; health is", player.health);
}
});
loop.done(function () { console.log("All enemies were added, and player is still alive!"); });
loop.fail(function (message) { console.log(message) });
// Stop the looper after 30 seconds
setTimeout(loop.resolve, 30000);

Related

Run a function when animation completes using Curtains.js

I'm looking to call a function right at the end of the wobble effect.
That is, at the end of the damping effect (when the wobble stops), I'd like to execute a GSAP timeline function. I'd assume this type of "onComplete" function would need to be called inside the onReady() of Curtains and perhaps by tracking the damping effect. I'm only familiar with GSAP's onComplete function, but don't know how I would implement it here. Maybe something that checks if deltas.applied is less than 0.001, then the function is called?
Below is the code snippet (without the fragment and vertex shaders). Full working code here:
CodePen
class Img {
constructor() {
const curtain = new Curtains({
container: "canvas",
watchScroll: false,
});
const params = {
vertexShader,
fragmentShader,
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0,
},
prog: {
name: "uProg",
type: "1f",
value: 0,
}
}
}
const planeElements = document.getElementsByClassName("plane")[0];
this.plane = curtain.addPlane(planeElements, params);
if (this.plane) {
this.plane
.onReady(() => {
this.introAnim();
})
.onRender(() => {
this.plane.uniforms.time.value++;
deltas.applied += (deltas.max - deltas.applied) * 0.05;
deltas.max += (0 - deltas.max) * 0.07;
this.plane.uniforms.prog.value = deltas.applied
})
}
// error handling
curtain.onError(function() {
document.body.classList.add("no-curtains");
});
}
introAnim() {
deltas.max = 6;
//console.log("complete") <-- need an onComplete type function~!
}
}
window.onload = function() {
const img = new Img();
}
What you could use is some algebra :)
First off, you should simplify your deltas.max function like so:
deltas.max += (0 - deltas.max) * 0.07;
// Simplifies to
deltas.max -= deltas.max * 0.07;
// Rewrite to
deltas.max = deltas.max - deltas.max * 0.07;
// Rewrite to
deltas.max = deltas.max * (1 - 0.07);
// Simplifies to
deltas.max *= 0.93; // Much nicer :)
That is actually pretty important to do because it makes our work of calculating the end value of our time variable and the duration of our animation significantly Easier:
// Given deltas.max *= 0.93, need to calculate end time value
// endVal = startVal * reductionFactor^n
// Rewrite as
// n = ln(endVal / startVal) / ln(reductionFactor) // for more see https://www.purplemath.com/modules/solvexpo2.htm
// n = ln(0.001 / 8) / ln(0.93)
const n = 123.84;
// Assuming 60fps normally: n / 60
const dur = 2.064;
Once we have those values all we have to do is create a linear tween animating our time to that value with that duration and update the max and prog values in the onUpdate:
gsap.to(this.plane.uniforms.time, {
value: n,
duration: dur,
ease: "none",
onUpdate: () => {
this.deltas.applied += (this.deltas.max - this.deltas.applied) * 0.05;
this.deltas.max *= 0.93;
this.plane.uniforms.prog.value = this.deltas.applied;
},
onComplete: () => console.log("complete!")
});
Then you get "complete!" when the animation finishes!
To make sure that your Curtains animations run at the proper rate even with monitors with high refresh rates (even the ones not directly animated with GSAP) it's also a good idea to turn off Curtain's autoRendering and use GSAP's ticker instead:
const curtains = new Curtains({ container: "canvas", autoRender: false });
// Use a single rAF for both GSAP and Curtains
function renderScene() {
curtains.render();
}
gsap.ticker.add(renderScene);
Altogether you get this demo.
This won't be the best answer possible but you can take some ideas and insights from it.
Open the console and see that when the animation gets completed it gets fired only once.
//Fire an onComplete event and listen for that
const event = new Event('onComplete');
class Img {
constructor() {
// Added a instance variable for encapsulation
this.animComplete = {anim1: false}
//Changed code above
const curtain = new Curtains({
container: "canvas",
watchScroll: false,
});
const params = {
vertexShader,
fragmentShader,
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0,
},
prog: {
name: "uProg",
type: "1f",
value: 0,
}
}
}
const planeElements = document.getElementsByClassName("plane")[0];
this.plane = curtain.addPlane(planeElements, params);
if (this.plane) {
this.plane
.onReady(() => {
this.introAnim();
document.addEventListener('onComplete', ()=>{
//Do damping effects here
console.log('complete')
})
})
.onRender(() => {
this.plane.uniforms.time.value++;
deltas.applied += (deltas.max - deltas.applied) * 0.05;
deltas.max += (0 - deltas.max) * 0.07;
this.plane.uniforms.prog.value = deltas.applied
if(deltas.applied<0.001 && !this.animComplete.anim1){
document.dispatchEvent(event)
this.animComplete.anim1 = true
}
})
}
// error handling
curtain.onError(function() {
document.body.classList.add("no-curtains");
});
}
introAnim() {
deltas.max = 6;
}
}
window.onload = function() {
const img = new Img();
}
I've found a solution to call a function at the end of the damping (wobble) effect, that doesn't use GSAP, but uses the Curtains onRender method. Because the uTime value goes up infinitely and the uProg value approaches 0, By tracking both the uTime and uProg values inside the Curtains onRender method we can find a point (2 thresholds) at which the damping effect has essentially completed. Not sure if this is the most efficient way, but it seems to work.
.onRender(() => {
if (this.plane.uniforms.prog.value < 0.008 && this.plane.uniforms.time.value > 50) { console.log("complete")}
})
Thanks to the Curtains docs re asynchronous-textures, I was able to better control the timing of the wobble effect with the desired result every time. That is, on computers with lower FPS, the entire damping effect takes place smoothly, with an onComplete function called at the end, as well as on comps with higher frame rates.
Although, as mentioned there is less control over the length of the effect, as we are not using GSAP to control the Utime values. Thanks #Zach! However, using a "threshold check" inside the curtains onRender this way, means the damping wobble effect is never compromised, if we were to disable the drawing at the on complete call.
By enabling the drawing at the same time the image is loaded we avoid any erratic behaviour. The following works now with hard refresh as well.
export default class Img {
constructor() {
this.deltas = {
max: 0,
applied: 0,
};
this.curtain = new Curtains({
container: "canvas",
watchScroll: false,
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
this.params = {
vertexShader,
fragmentShader,
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0,
},
prog: {
name: "uProg",
type: "1f",
value: 0,
},
},
};
this.planeElements = document.getElementsByClassName("plane")[0];
this.curtain.onError(() => document.body.classList.add("no-curtains"));
this.curtain.disableDrawing(); // disable drawing to begin with to prevent erratic timing issues
this.init();
}
init() {
this.plane = new Plane(this.curtain, this.planeElements, this.params);
this.playWobble();
}
loaded() {
return new Promise((resolve) => {
// load image and enable drawing as soon as it's ready
const asyncImgElements = document
.getElementById("async-textures-wrapper")
.getElementsByTagName("img");
// track image loading
let imagesLoaded = 0;
const imagesToLoad = asyncImgElements.length;
// load the images
this.plane.loadImages(asyncImgElements, {
// textures options
// improve texture rendering on small screens with LINEAR_MIPMAP_NEAREST minFilter
minFilter: this.curtain.gl.LINEAR_MIPMAP_NEAREST,
});
this.plane.onLoading(() => {
imagesLoaded++;
if (imagesLoaded === imagesToLoad) {
console.log("loaded");
// everything is ready, we need to render at least one frame
this.curtain.needRender();
// if window has been resized between plane creation and image loading, we need to trigger a resize
this.plane.resize();
// show our plane now
this.plane.visible = true;
this.curtain.enableDrawing();
resolve();
}
});
});
}
playWobble() {
if (this.plane) {
this.plane
.onReady(() => {
this.deltas.max = 7; // 7
})
.onRender(() => {
this.plane.uniforms.time.value++;
this.deltas.applied += (this.deltas.max - this.deltas.applied) * 0.05;
this.deltas.max += (0 - this.deltas.max) * 0.07;
this.plane.uniforms.prog.value = this.deltas.applied;
console.log(this.plane.uniforms.prog.value);
// ---- "on complete" working!! ( even on hard refresh) -----//
if (
this.plane.uniforms.prog.value < 0.001 &&
this.plane.uniforms.time.value > 50
) {
console.log("complete");
this.curtain.disableDrawing();
}
});
}
}
destroy() {
if (this.plane) {
this.curtain.disableDrawing();
this.curtain.dispose();
this.plane.remove();
}
}
}
const img = new Img();
Promise.all([img.loaded()]).then(() => {
console.log("animation started");
});

setInterval time not getting correct value

I have two arrays. One of files and another for time I want to display them in an iframe. I loop through them the files appear correctly but the time is always set to the first element.
The iframe always loads for 10000 ms.
$(function() {
var urls = ['/uploads/presentations/1560837902.pdf', '/uploads/presentations/1560837925.mp4', '/uploads/presentations/1560837959.jpg', '/uploads/presentations/1560838138.docx', '/uploads/presentations/1560838215.ppt'];
var time = [10000, 40000, 10000, 20000, 10000];
var i = 0;
function loadIframe(url) {
$('#iframe').attr('src', url);
}
setInterval(function() {
// update the index
i = (i + 1) % urls.length;
loadIframe(urls[i]);
}, time[i]);
loadIframe(urls[i]);
});
The issue is because you only define a single interval, hence the delay is always the same.
To fix this you could use a recursive timeout instead, defining the next when the previous fires. Try this:
$(function() {
var urls = ['/uploads/presentations/1560837902.pdf', '/uploads/presentations/1560837925.mp4', '/uploads/presentations/1560837959.jpg', '/uploads/presentations/1560838138.docx', '/uploads/presentations/1560838215.ppt'];
var time = [10000, 40000, 10000, 20000, 10000];
function loadIframe(i = 0) {
$('#iframe').attr('src', urls[i % urls.length]);
console.log(`Changing src to ${urls[i % urls.length]}`);
setTimeout(function() {
loadIframe(++i);
}, time[i % time.length]);
}
loadIframe();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

JavaScript game switchable sprites

Any ideas how to make switchable characters I have a html game it's finished but I want to implement a way to switch my main character.
Simple coding using Phaser framework
upload function() {
this.game.load.sprite ("bird" assets/bird.png);
this.game.load.sprite ("bird2" assets/bird2.png);
this.game.load.sprite ("bird3" assets/bird3.png);
},
create function() {
this.game.add.sprite (0, 0 "bird" );
},
I want to be able to switch my playable character the "bird" with the "bird2" or "bird3" through a selection button if a player selects a switch character button for the playable character to switch to that. I'm pretty sure this is something simple but I'm still pretty new with coding.
I want a button where I press then I can switch the character
(Button 1) switches to bird2
"if button 1 is selected button two and current bird are disabled"-only bird2 is visible
(Button 2) switches to bird3
"if button 2 is selected button one and current bird are disabled"-only bird3 is visible
Edit This is My current code and states
var MainState = {
//load the game assets before the game starts
preload: function () {
this.load.image('background', 'assets/spring2.png');
this.load.spritesheet('bird', 'assets/bird.png',52 ,28, 7);
this.load.spritesheet('bird2', 'assets/bird2.png',52 ,28, 7);
this.load.spritesheet('bird3', 'assets/bird3.png',52 ,28, 7);
this.load.image('pipe', 'assets/pipe4.png');
},
//executed after everything is loaded
create: function () {
this.background = game.add.tileSprite(0, game.height-736,game.width, 736, 'background');
this.background.autoScroll(-20,0);
/////Bird///////////////////////////////////////////////////
this.bird = this.game.add.sprite(100, 200, 'bird');
this.bird.animations.add('fly');
this.bird.animations.play('fly', 50, true);
game.physics.startSystem(Phaser.Physics.ARCADE);
game.physics.arcade.enable(this.bird);
this.bird.body.gravity.y = 1000;
var spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
this.bird.body.collideWorldBounds=true;
this.bird.body.immovable= true;
game.input.onDown.add(this.jump, this); //////touch screen jump
spaceKey.onDown.add(this.jump, this);
///////////////////////////////////////////////////////Pipes
this.pipes = game.add.group();
//timer
this.timer = game.time.events.loop(1600, this.addRowOfPipes, this); /////////////timer for pipes
///Bird anchor
this.bird.anchor.setTo(-0.2, 0.5)
},
// this is execated multiple times per second
update: function () {
if (this.bird.y < 0 || this.bird.y > 480)
game.state.start("StateOver");
///Collision
game.physics.arcade.overlap(
this.bird, this.pipes, this.restartGame, null, this);
///Bird Angle
if (this.bird.angle < 30)
this.bird.angle += 1;
///////////////music stop w top+bottom collision
if (this.bird.y < 0 || this.bird.y > 479)
music.stop();
},
jump: function () {
//this is for so the bird wount fly once dead
if (this.bird.alive == false)
return;
// Add a vertical velocity to the bird
this.bird.body.velocity.y = -350;
// Jump Animation
var animation = game.add.tween(this.bird);
// Change the angle of the bird to -20° in 100 milliseconds
animation.to({angle: -20}, 100);
// And start the animation
animation.start();
game.add.tween(this.bird).to({angle: -20}, 100).start();
},
restartGame: function () {
// Start the 'main' state, which restarts the game
game.state.start(game.state.StateOver); /////////////////////changed from current #########
///Hit pipe Null
game.physics.arcade.overlap(
this.bird, this.pipes, this.hitPipe, null, this);
},
addRowOfPipes: function() {
var hole = Math.floor(Math.random() * 5) + 1; ///Math.floor(Math.random() * 5) + 1;
for (var i = 0; i < 10 ; i++) ///// (var i = 0; i < 8; i++)
if (i != hole && i != hole + 1) ///// if (i != hole && i != hole + 1)
this.addOnePipe(440, i * 50 ); ///// 640 starting point of pipe 240 point of down ////this.addOnePipe(480, i * 60 + 10);
},
addOnePipe: function(x, y) {
var pipe = game.add.sprite(x, y, 'pipe');
this.pipes.add(pipe);
game.physics.arcade.enable(pipe);
pipe.body.velocity.x = -200;
pipe.checkWorldBounds = true;
pipe.outOfBoundsKill = true;
},
hitPipe: function() {
// If the bird has already hit a pipe, do nothing
// It means the bird is already falling off the screen
if (this.bird.alive == false)
return;
else {
game.state.start("StateOver");
}
// Set the alive property of the bird to false
this.bird.alive = false;
// Prevent new pipes from appearing
game.time.events.remove(this.timer);
// Go through all the pipes, and stop their movement
this.pipes.forEach(function(p){
p.body.velocity.x = 0;
}, this);
},
};
character.js
var characters={
preload:function()
{
game.load.spritesheet('button', 'assets/button.png', 215, 53, 8);
game.load.image("background", "assets/characterbackground.png");
game.load.image("pajaro", "assets/storeicon.png");
game.load.image("logo", "assets/extra/storef.png");
this.load.spritesheet('bird', 'assets/bird.png',52 ,28, 7);
this.load.spritesheet('bird2', 'assets/bird2.png',52 ,28, 7);
this.load.spritesheet('bird3', 'assets/bird3.png',52 ,28, 7);
game.load.spritesheet("button2", 'assets/button2.png', 100, 10, 10);
},
create:function()
{
bird = game.add.image(140, 150, 'pajaro');
logo = game.add.image (20, 350, 'logo');
this.background = game.add.tileSprite(0, game.height-736,game.width, 736, 'background');
this.background.autoScroll(-100,0);
this.btnMainMenu=game.add.button(130,500,'button',this.mainMenu,this,4,5,4);
this.btnbird=game.add.button(180,600,"button2",this.changebird2,this,0,1,0);
},
mainMenu:function()
{
game.state.start("stateTitle");
},
update:function()
{
// bird.x +=1;
},
changebird2: function(){
},
};
Instead of creating three sprites that you either hide or show, I might recommend just changing what texture is loaded when the sprite is created/added.
To do this you'll need to store a reference to the playable character, which you probably already have.
// On the game itself, add a reference.
this.bird = null;
// In your preload, load the different images.
this.load.image('bird', 'assets/bird.png');
this.load.image('bird2', 'assets/bird2.png');
this.load.image('bird3', 'assets/bird3.png');
// When creating, default to one.
this.bird = this.game.add.sprite(0, 0, 'bird');
// In your function where they select a new skin, you can load in a different texture.
this.bird.loadTexture('bird3');
Alternatively, you could store the key that should be used on the game.
// On the game itself, track which key to use.
this.birdSkin = 'bird';
// You'll still have to load your possible textures.
this.load.image('bird', 'assets/bird.png');
this.load.image('bird2', 'assets/bird2.png');
this.load.image('bird3', 'assets/bird3.png');
// Now when creating just use the variable.
this.bird.loadTexture(this.birdSkin);
The Phaser init() will allow 0 or more parameters to be passed in (see the end of Phaser Tutorial: understanding Phaser states), which is where you could populate this.birdSkin.
I would look at what states you're using to determine what's best for you. If you have one state for the game and another for selecting which image/texture is used, than the second option might be better.
Update for Character State
Given your comments and what I saw in your code, I created a short example that you could tweak for your use.
There's a JSFiddle available, but the code is also included below.
var mainState = {
preload: function() {
// Load the three sprites that they can choose between.
this.load.crossOrigin = 'anonymous';
this.load.image('ball', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-blue.png');
this.load.image('ball2', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-green.png');
this.load.image('ball3', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-red.png');
},
create: function() {
this.ball = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, this.game.global.skin);
this.ball.anchor.setTo(0.5);
// Let the ball be acted upon. This will allow the player to change the sprite used.
this.ball.inputEnabled = true;
this.ball.events.onInputDown.add(this.changeCharacter, this);
},
update: function() {
},
changeCharacter: function() {
game.state.start('character');
}
};
var characterState = {
preload: function() {
},
create: function() {
// For this, add our three possible ball skins.
this.ball1 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY / 2, 'ball');
this.ball1.anchor.setTo(0.5);
this.ball1.inputEnabled = true;
this.ball2 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'ball2');
this.ball2.anchor.setTo(0.5);
this.ball2.inputEnabled = true;
this.ball3 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY * 1.5, 'ball3');
this.ball3.anchor.setTo(0.5);
this.ball3.inputEnabled = true;
// Use the selected ball's sprite in our main game.
this.ball1.events.onInputDown.add(this.selectBall, this);
this.ball2.events.onInputDown.add(this.selectBall, this);
this.ball3.events.onInputDown.add(this.selectBall, this);
},
update: function() {
},
selectBall: function(sprite, pointer) {
// Grab the key of the sprite and save it to our global variable.
this.game.global.skin = sprite.key;
this.game.state.start('main');
}
};
var game = new Phaser.Game(200, 200);
// Create a global object that we can add custom variables to.
game.global = {
skin: 'ball'
};
game.state.add('main', mainState);
game.state.add('character', characterState);
game.state.start('main');
This actually simplifies things a bit, in that it just uses a global variable (I've been using TypeScript the last handful of months, so there's probably a better way to declare this).

Changing images using delays

I’m trying to figure out how to make an animation using the order of three images.
The default image is “image1.png” that always shows when the page loads.
After every 5 seconds, the variable “back.src” must abruptly change
to image2.png, so without fading. The default is image1.png
And then after 0.5 seconds, the variable again changes but then to
image3.png.
0.5 seconds later it changes back to image2.png
and 0.5 later again back to image1.png.
This is to be repeated in a loop because I want to repeat the process again after 5 seconds.
My problem is, I don't know if structuring this code is the best way to go about it. How would my code need to look based on the requirement explained above?
Here's my code of what I've got so far:
var back = new Image();
back.src = "image1.png";
function wait(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {
}
}
function image1() {
wait(5000);
back.src = "image2.png";
}
function image2() {
wait(500);
back.src = "image3.png";
}
function image3() {
wait(500);
back.src = "image2.png";
}
function animate(){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(back,0,0);
ctx.restore();
}
var animateInterval = setInterval(animate, 30);
There is no wait() operation in Javascript and usually trying to make one like you are doing causes bad things to happen (event loops get starved, user interfaces get locked up, etc...). Instead, you schedule things to run in the future with setTimeout(). This allows the JS engine to do other things (like service other events happening in the system) while you are waiting for your next loop iteration and is generally very important in Javascript.
I'd suggest you just put the sequence you want into a data structure and then use a timer that iterates through the data structure, wrapping when it gets to the end:
var data = [
["image1.png", 5000],
["image2.png", 500],
["image3.png", 500],
["image4.png", 500]
];
function runAnimation() {
var index = 0;
function animate(image){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(image,0,0);
ctx.restore();
}
function next() {
var img = new Image();
img.src = data[index][0];
animate(img);
// schedule next iteration
var t = data[index][1];
// increment and wrap index if past end
index = (index + 1) % data.length;
setTimeout(next, t);
}
next();
}
To make this work properly, you will need your images to be precached so they get loaded immediately. If you aren't going to precache the images, then you will need to add onload handlers so you can know when the images have finished loading and are ready for drawing.
There is info on precaching images here: How do you cache an image in Javascript
Or, to make sure your images are loaded before drawing with them, you can use an onload handler like this:
var data = [
["image1.png", 5000],
["image2.png", 500],
["image3.png", 500],
["image4.png", 500]
];
function runAnimation() {
var index = 0;
function animate(image){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(image,0,0);
ctx.restore();
}
function next() {
var img = new Image();
var nextSrc = data[index][0];
img.onload = function() {
animate(img);
// schedule next iteration
var t = data[index][1];
// increment and wrap index if past end
index = (index + 1) % data.length;
setTimeout(next, t);
};
img.src = nextSrc;
}
next();
}
You can achieve it using setIterval()
Read about JS Timers Here
Here is what you need : https://jsfiddle.net/nfe81zou/3/
var image1 = "https://pixabay.com/static/uploads/photo/2016/06/17/13/02/duck-1463317_960_720.jpg";
var image2 = "https://pixabay.com/static/uploads/photo/2013/09/22/16/56/duck-185014_960_720.jpg";
var image3 = "https://pixabay.com/static/uploads/photo/2013/11/02/03/23/ducks-204332_960_720.jpg";
$(function() {
$("#image").prop("src", image1);
setInterval(function() {
$("#image").prop("src", image2);
setTimeout(function() {
$("#image").prop("src", image1);
setTimeout(function() {
$("#image").prop("src", image3);
}, 500);
setTimeout(function() {
$("#image").prop("src", image2);
}, 1000);
}, 1500);
}, 5000);
});
#image {
border: 2px solid red;
width: 400px;
height: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img src="" id="image"/>
This would be my approach on this problem. The images are displayed in the order of
0 (5000ms) -> 1 (500ms) -> 2 (500ms) -> 1 (500ms) -> 0 (5000ms) ...
There is no setInterval() here. This code utilizes a pseudo recursive setTimeout() implementation.
var images = ["http://1.bp.blogspot.com/_ZRj5nlc3sp8/S-T7vOsypQI/AAAAAAAADBo/0kYfB8BM6zE/s320/beautiful+girl+pics+1.jpg",
"http://1.bp.blogspot.com/-w4UtHwbrqBE/ViNbJWhnmvI/AAAAAAAADtY/hGbRtz993Tg/s1600/face%2Bwallpapers%2B%25282%2529.jpg",
"http://2.bp.blogspot.com/-4kZ9Iu8QTws/ViNbL3S29fI/AAAAAAAADtw/QakqQE72N1w/s1600/face%2Bwallpapers%2B%25286%2529.jpg"],
imel = document.getElementById("photo");
function displayImages(idx,fwd){
var ms = 0;
idx = idx || 0; // we could also use the ES6 default value method.
if (idx === 0) {
fwd = !fwd; // when index becomes 0 switch the direction of the loop
ms = fwd ? 5000 // if backwards rearrange index and duration to 0.5 sec
: (idx = images.length - 2, 500);
} else ms = 500; // when index is not 0 set the duration to 0.5 sec
imel.src = images[idx];
fwd ? setTimeout(function(){displayImages(++idx % images.length,fwd)},ms)
: setTimeout(function(){displayImages(--idx,fwd)},ms);
}
displayImages();
<image id ="photo"></image>

How should multiple progress bars be handled in javascript

I have a number of progress bars each tied to a div which are updated using 'setTimeouts'.
An example of how it runs is like this :
myDiv._timer = setTimeout(function () {
func_name(data)
}, 1);
Edit: As requested a working example of my one progress bar: http://jsfiddle.net/H4SCr/
The question however is, I have multiple div's with progression bars with their own data to use to calculate progression. Which means with say 5 on the go i have 5 different timeouts running.
I'm no expert in javascript, but surely theres a way to structure this to tie to just one time out for all progress bars, or is my current approach the best method ?
Note: i don't use jQuery. I prefer to go with just vanilla javascript to learn!
Check this out:
http://jsfiddle.net/MZc8X/11/
I created an array of objects which contains the container id and its increment value.
// array to maintain progress bars
var pbArr = [{
pid: 'bar1', // parent container id
incr: 1 // increment value
}, {
pid: 'bar2',
incr: 2
}, {
pid: 'bar3',
incr: 3
}, {
pid: 'bar4',
incr: 4
}, {
pid: 'bar5',
incr: 5
}];
And, then call a function to create a progress bar...
var loopCnt = 1; // loop count to maintain width
var pb_timeout; // progress bar timeout function
// create progress bar function
var createPB = function () {
var is_all_pb_complete = true; // flag to check whether all progress bar are completed executed
for (var i = 0; i < pbArr.length; i++) {
var childDiv = document.querySelector('#' + pbArr[i].pid + ' div'); // child div
var newWidth = loopCnt * pbArr[i].incr; // new width
if (newWidth <= 100) {
is_all_pb_complete = false;
childDiv.style.width = newWidth + '%';
} else {
childDiv.style.width = '100%';
}
}
if (is_all_pb_complete) { // if true, then clear timeout
clearTimeout(pb_timeout);
return;
}
loopCnt++; // increment loop count
// recall function
pb_timeout = setTimeout(function () {
createPB();
}, 1000);
}
// call function to initiate progress bars
createPB();
Hope, it works for you.

Categories

Resources