How do I make a loop animation stop and resume? - javascript

I have a JavaScript code that I am going to use for an animated train map.
I have a loop that goes up and down, but I would like it to stop for 10 seconds every 20px, and then resume it's movement.
How do I accomplish this?
`window.onload = () => {
startSetTrain0Animation();
startSetTrain1Animation();
};
function startSetTrain0Animation() {
const refreshRate = 1000 / 60;
const maxXPosition = 470;
let rect = document.getElementById('rect0');
let speedX = 0.02;
let positionX = 25;
window.setInterval(() => {
positionX = positionX + speedX;
if (positionX > maxXPosition || positionX < 25) {
speedX = speedX * (-1);
}
rect.style.top = positionX + 'px';
}, refreshRate);
}`

There are typically two options to handle this:
Use a flag to indicate the state of the loop
Store a reference to the setInterval() function so that you can clear/stop via the clearInterval() function and later restart.
Using a Flag
Using a flag would be as simple as it sounds. Simply declare a variable and it would dictate if the body of the loop is executed or not:
// Flag to pause animation (change its value to toggle the animation)
var paused = false;
// Omitted for brevity
window.setInterval(() => {
// If you are paused, don't do anything
if (paused) {
return;
}
positionX = positionX + speedX;
if (positionX > maxXPosition || positionX < 25) {
speedX = speedX * (-1);
}
rect.style.top = positionX + 'px';
}, refreshRate);
This is the simplest of use cases, however you could easily adjust it to define some logic regarding when you would start/stop within the interval itself.
Explicitly Stopping / Restarting the Loop
Another option would be to store a reference to your loop in a variable when you declare it:
// Store a reference to your loop
var loop = setInterval(() => { ... }, refreshRate);
// Clear it when you need to stop it
window.clearInterval(loop);
If you prefer this approach, you'd likely elect to have a function that would wrap your "start loop" functionality such that you could do something like:
// Start the loop (wraps your setInterval() function)
var loop = startLoop();
// Stop the loop (could easily be renamed stopLoop())
window.clearInterval(loop);

Related

Passing default value inside arrow function

Im working with this part of code which one render simple loading bar
const smallSpinner = document.getElementById('spinner-small').getContext('2d');
let pointToFill = 4.72;
let cw = smallSpinner.canvas.width; //Return canvas width
let ch = smallSpinner.canvas.height; //Return canvas height
let diff;
let = fillSmallSpinner = (startingPointSmall = 0) => {
diff = ((startingPointSmall / 100) * Math.PI * 2 * 10);
smallSpinner.clearRect(0, 0, cw, ch);
smallSpinner.lineWidth = 5;
smallSpinner.strokeStyle = '#d40511';
/* smallSpinner.textAlign = 'center';
smallSpinner.font = "25px monospace";
smallSpinner.fillText(no + '%', 50, 55); */ //uncomment this if you need percent progress inside spinner
smallSpinner.beginPath();
smallSpinner.arc(50, 50, 40, pointToFill, diff / 10 + pointToFill);
smallSpinner.stroke();
if (startingPointSmall >= 100) {
clearTimeout(fill);
}
startingPointSmall++;
}
let small = setInterval(fillSmallSpinner, 50);
The point is that when "startingPointSmall" is defined like normal variable
let startingPointSmall = 0;
it works totaly fine but i want to make this a little bit more usable and pass the starting point as a function parameter. When i do this like this with predefined starting point on 0% it doesnt work. Can someone explain me how to fix this?
Every time that setInterval queues up a call to fillSmallSpinner it will receive its default parameter - over and over!
A more common pattern is to wrap the function in a way that preserves the desired variable's scope:
const startFiller(callback, interval = 50, start = 0) {
let current = startPoint;
let timer = setInterval(() => {
callback(current++);
if (current >= 100) {
clearTimeout(timer);
}
}, interval);
});
startFiller(fillSmallSpinner);
You would then remove any existing timer-related logic from your fillSmallSpinner function. This approach has the added benefit of Separation of Concerns - if you decide you want to use a different function to render your spinner it no longer needs to concern itself with timers.

How to tell on which part of the screen divided to N rectangles am I (with mousemove)

I need to divide screen to 20 parts (horizontally) and set the value of mouse position from 1 to 20 to update sprite background-image position (for a smooth rotation animation). The code below is working, but there is a problem, when I move mouse very fast - than it can skip a few points, and I need to always change the number by one step. How can I achieve that?
https://codepen.io/kgalka/pen/vbpoqe
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
updatePosition(Math.round(e.clientX / frameWidth));
}
Ok i saw th example and i think that i understand the problem and here is how i would fix this.
Have a look and let me know if it work.
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
var timeout;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
// clear the prev call if the mouse was to fast.
clearTimeout(timeout);
// Now ini new call to updatePosition
timeout= setTimeout(()=> updatePosition(Math.round(e.clientX / frameWidth)), 1 )
// you could play with the timeout and increase it from 1 to 100ms. and see what you prefere
}
<p id="val"></p>

bad three js performance with raycasters

so i have been experimenting with ray casting in three js but i ran into terrible performance issues in Firefox and chrome (but something else is causing chromes camera to rubber band even though its a small local game) anyways when i add this code to the animate loop
var intersects = raycaster.intersectObjects(sceneObjects);
if (intersects.length > 0) {
var firstIntersectedObject = intersects[0];
console.log(intersects[0].object.userData)
console.log(intersects[0].object.userData.glow )
if (intersects[0].object.userData.glow === 'true'){
console.log("GLOW")
}else{
console.log("NO!")
}
//intersects[0].object.material.wireframe = true
// this will give you the first intersected Object if there are multiple.
}
my game starts to get all laggy and i have no idea why any pointers
Don't raycast on every frame, instead you should have it raycast on an interval. You can use setTimeout or setInterval or check the timing in the update loop.
onUpdate() {
// Code that runs once per frame
// Check that we've waited long enough to raycast
if (Date.now() - this.lastRaycast > this.raycastInterval && this.qRaycast) {
this.handleRaycast();
this.lastRaycast = Date.now();
this.qRaycast = false;
}
requestAnimationFrame( () => this.onUpdate() );
}
I also only queue up raycasts when the mouse moves (no reason to keep raycasting if the mouse isn't moving) and because I have panning in my project, I disable raycast during panning movements to prevent any jitter during movement.
// Event Handlers
// Record mouse position for raycast
onMouseMove(e) {
this.mouse.x = (e.clientX / window.innerWidth ) * 2 - 1;
this.mouse.y = -((e.clientY - 50) / window.innerHeight ) * 2 + 1;
// If we are panning, don't queue a raycast
this.qRaycast = !this.mouseState.held;
}
// My app has panning, and we don't wanna keep raycasting during pan
onMouseDown(e) {
this.mouseState.lastClick = Date.now();
this.mouseState.clicked = false;
this.mouseState.held = true;
}
onMouseUp(e) {
this.mouseState.held = false;
}
then we handle the raycast:
// Like lasers, but virtual.
handleRaycast() {
let hits = null;
let hitcount = 0;
if (UI.raycast && meshObj) {
this.raygun.setFromCamera(this.mouse, this.camera);
hits = this.raygun.intersectObject(meshObj, false);
hitcount = hits.length;
}
if (hitcount > 0) {
// Do stuff with the raycast here
}
}
If you are still having performance issues, then you might wanna look into breaking down that loop function so that after XXms it breaks to let the UI update, and then continues updating on the next frame:
For example, I sort through all hits and find the point that is closest to the mouse:
// Optimization
let startTime = 0;
let maxTime = 75; // max time in ms
let dist = 1;
let hitIndex;
let i = 0;
function findClosest() {
return new Promise((resolve) => {
function loop() {
startTime = performance.now();
while (i < hitcount) {
// Break loop after maxTime
let currentTime = performance.now();
if ((currentTime - startTime) > maxTime) {
console.log('Loop exceeded max time: ' +
(currentTime - startTime).toFixed(3) );
startTime = currentTime;
break;
}
// I am finding the raycast point that is closest to the cursor
dist = hits[i].distanceToRay;
if (dist < smallestDist) {
smallestDist = dist;
smallestPointIndex = hits[i].index;
}
i++;
}
if (i < hitcount) {
// Allow the UI to update, then loop
setTimeout(loop, 1);
} else {
resolve(smallestPointIndex);
}
}
loop();
});
}
findClosest().then(result => {
// do something with the result here
}
Also the comments were good suggestions also to reduce the number of objects you are raycasting to.

Phaser Game Framework - Sprite.y value changed in 2 update loops despite conditional

This is a simple test case from a game in phaser.js. The ball moves to top of screen, collides, bounces down and stops because of a conditional, I set the sprite's y value and velocity to stop it falling further.
However, in the next update loop, the y value increases again, and count increases to 2.
From console.log - as ball descends y is 437,442,445, then remains on 440. I don't understand how ball.y goes beyond 440 twice, this is the entire running code. Setting bounce to 0 in conditional has no effect. It's not that console.log in the browser is out of sync with phaser update, because the count variable reports the conditional is entered twice.
var mainState = {
preload: function() {
game.load.image('ball', 'assets/ball.png');
},
create: function() {
game.physics.startSystem(Phaser.Physics.ARCADE);
game.world.enableBody = true;
ball = game.add.sprite(180, 440, 'ball');
ball.body.bounce.setTo(1);
ball.body.allowGravity = false;
ball.body.collideWorldBounds = true;
game.physics.arcade.moveToXY(ball, 160,10,350); ballstop = false;
count = 0;
},
update: function() {
console.log("ball.y " + ball.y);
if (ball.y > 440) {
ball.body.bounce.setTo(0);
ball.body.velocity.x = 0;
ball.body.velocity.y = 0;
ball.y = 440;
count = count + 1;
}
},
};
var game = new Phaser.Game(360, 640);
game.state.add('main', mainState);
game.state.start('main');
Try reseting the body.
The relevant block would look something like this:
if (ball.y > 440) {
ball.body.bounce.setTo(0);
ball.body.reset(0, 0);
//ball.body.velocity.x = 0;
//ball.body.velocity.y = 0;
ball.y = 440;
count = count + 1;
}
I've also saved a JSFiddle with this change.

Make gravity in HTML5 canvas work twice

This is my JSFiddle: http://jsfiddle.net/Au6br/13/
The problem is when I keep pressing keydown.up the player jump multiple times. What I want is to define a variable called JumpCount so when the player jump if the jump variable is greater than this value the jumping stop.
Character.prototype.Jump = function () { // Jumping
character.MaxJump += gravity;
character.y += character.MaxJump;
if (keydown.up) {
if (!character.jumping) {
character.jumping = true;
character.Pos = 4;
character.Row = 2;
character.h = 23;
character.MaxJump = -character.sp * 2.5;
}
}
if (character.y >= ch - character.h) { // if the character is under the bottom
character.y = ch - character.h;
character.jumping = false;
}
};
The first problem is that you can jump when you're not on the ground. It's because of the Onground function.
Replace this :
return this.y <= ch - this.h;
With this :
return this.y >= ch - this.h;
You should also use this function in Jump to avoid duplicate code :
if (character.Onground()) { // if the character is under the bottom
character.y = ch - character.h;
character.jumping = false;
}
Counting the jumps (I assume you want to make double jumps) can't work as long as you test if(keydown.up). When you press the UP key, this value will be true for more than one frame (depending on the duration the player presses it), so you'll never be able to correctly count the number of jumps. You must bind the jump on the onkeydown event, which is called once. After that, it will be simple to replace character.jumping with an integer.
I'd do it like this:
Character.prototype.Jump = function () {
character.MaxJump += gravity;
character.y += character.MaxJump;
if (keydown.up) {
if (character.CurrentJumps < character.JumpCount) {
character.CurrentJumps++
character.Pos = 4;
character.Row = 2;
character.h = 23;
character.MaxJump = -character.sp * 2.5;
}
}
if (character.y >= ch - character.h) {
character.y = ch - character.h;
character.CurrentJumps = 0
}
};
Were JumpCount is the max jumps, and CurrentJumps is the currently done jumps
And an example to set to double jump:
character.JumpCount = 2 // 2 Jumps
How about you add a variable called jumpCount or something that you increase every time the character jumps. Only allow the character to jump if jumpCount is smaller or equal to the number of jumps you like the character to be able to do. Then just set jumpCount to zero when the player touches the floor. This also gets rid of your jumping variable since this would mean the same thing as jumpCount === 0.

Categories