Make requestAnimationFrame() Animation Persist When on Different Tab - javascript

When you are using requestAnimationFrame() for animation, the animation persists when you change browsers but not when you change tabs within your browser. The animation seems to pause.
For example, this animation.
http://pixijs.github.io/examples/index.html?s=basics&f=spritesheet.js&title=SpriteSheet%20Animation
Is there a way to make the animation persist even when you are in a different tab?

Your question isn't actually PIXI specific. It's about how the browser handles screen updates.
PIXI applications typically update each frame using the common Javascript function Window.requestAnimationFrame().
This function tends to pause when you switch tabs. From the docs:
The callback rate may be reduced to a lower rate when running in background tabs or in hidden <iframe>s in order to improve performance and battery life.
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
So the short answer is no,
HOWEVER you can easily simulate it by keeping keep track of the time elapsed between updates, and move your animation forward accordingly.
In the PIXI example you provided, change the animate() function to this code below, and re-run it. Switch tabs, and when you switch back, you will see that the animation appears to continue as though you never switched tabs. Hope that helps! :)
var lastUpdate = new Date();
function animate() {
//Determine the amount of time since last frame update
var now = new Date();
var elapsed = now - lastUpdate;
lastUpdate = now;
//Update the rotation based on time elapsed
movie.rotation += elapsed/1000;
// render the stage container
renderer.render(stage);
requestAnimationFrame(animate);
}

Related

Web audio api: smooth transitions between audio fades in/out

I have a 3d space with different zones, each zone has its own sounds. My goal is to have smooth audio transitions when the user moves from one zone to another, basically doing cross-fades between the audio tracks.
Also I would like to handle gracefully the case when the user changes its mind and abruptly goes back to the zone just left, this means cancel the new audio transitions and revert back to the previous state gradually.
To do this I am using web audio api, in particular linearRampToValueAtTime and cancelScheduledValues to define the fades in/out functions:
const fadeIn = (sound, time = 4, gain = 1.0) => {
sound.gainNode.gain.cancelScheduledValues(sound.context.currentTime)
!sound.isPlaying && sound.play()
sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time)
}
const fadeOut = (sound, time = 4, gain = 0.0) => {
sound.gainNode.gain.cancelScheduledValues(sound.context.currentTime)
sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time)
}
I am testing this on just one sound, pressing key "I" triggers fadeIn and key "O" triggers fadeOut:
document.addEventListener("keyup", e => {
switch (e.keyCode) {
// I key
case 73:
fadeIn(mySound)
break
// O key
case 79:
fadeOut(mySound)
break
})
I got inconsistent results. Sometimes the audio fades correctly, other times it starts abruptly in the fadeIn or cut in the fadeOut. It seems like calling cancelScheduledValues when a transition has not been completed results in an erratic behaviour. I would like this to work smoothly even when triggering a fadeOut when a fadeIn has not completed yet and viceversa. I have also tried to defer a bit the linearRamps with setTimeout, something like
setTimeout(() => sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time), 0.1)
but got equally wrong results.
Am I using the api incorrectly? Any suggestion?
I think what's happening here is that, say, you're fading in and you haven't finished the ramp yet. Then you want a fade out, and cancel the events, including the unfinished fade-in ramp. Then that fade-in ramp is gone, and the timeline value reverts back to the value of the previous event.
So, as HankMoody suggests, use cancelAndHoldAtTime which is meant to handle this problem. But, AFAIK, only Chrome has this.
To be portable, I think what you want to do is compute where the ramp is at the time you want to cancel. Let's say the value is v. Then do
setValueAtTime(v, context.currentTime);
cancelScheduledValues(context.currentTime + eps);
where eps is some small value around 1/context.sampleRate or more.

JavaScript scroll based animation is choppy on mobile

I have 2 divs (left and right) and i want to scroll the left based on the right.
https://jsfiddle.net/3jdsazhg/2/
This works fine on desktop, but when i change to mobile, it's not smooth anymore...
This can be noticed very easily, by changing
_left.style.top = _content.scrollTop - (_content.scrollTop * ratioLeftRight) + 'px';
to
_left.style.top = _content.scrollTop + 'px';
Where it should act as a fixed positioned div
I would like to know the exact reason why this isn't smooth... I know that it's not the animation. Simple animation on the div is smooth, the issue comes up when it's based on scroll.
How can i make this animation smooth?
It's probably choppy because it's being fired ALOT when being scrolled, in fact i'm pretty sure IOS mobile pauses the javascript execution whilst the user is scrolling.
Instead I'd suggest using an interval, you could tweak the time between each interval to what feels good for your use-case.
Although it may seem intensive that it's firing this logic every X millisecond when using the scroll event you could be firing the event off hundreds of times per second, which is going to be far more intensive and noticeable to a user using a device with limit processing power.
(function () {
var interval = null,
//Currently set at 0.4 seconds, play with the code
//and change this value to see what works best for
//this use-case
time_between_interval = 400;
setInterval(scrollLogic, time_between_interval);
function scrollLogic () {
//The function body of what you're assigning
//to the scroll event.
}
//I have omitted clearing the interval but you would want to do that, perhaps on page change, or something.
//clearInterval(interval);
})();
I finally managed to think out a solution.
From my point of view, i'm guessing the mobile view fires the scroll event less often and because we are scrolling the wrapper, we first scroll the whole page and then scroll back with js the left part and because it's different from the desktop version, this issue becomes visible...
The solution was to change the left side to fixed position, and substract from the top instead of adding to it.
_left.style.top = -(_content.scrollTop * ratioLeftRight) + 'px';

CSS/JS scrolling glitch effect (performance)

I am trying to achieve a "crt-like" scrolling glitch effect using Javascript and CSS. I have come up with the following code which clones the content and applies clip to make it look like it scrolls while adding random horizontal offset.
function scanglitch() {
var e = $('#wrapper').clone().appendTo('#glitchcontainer');
var i = 0;
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css("z-index",200);
var interval = setInterval(function () {
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css({"left": Math.round(Math.random() * 10) + "px"});
i+=4;
if (i > window.innerHeight) {
e.remove();
window.clearInterval(interval);
}
}, 40);
}
Fiddle (Click on the text to see the effect)
I am actually quite pleased with the effect, but the implementation is obviously a hack. As a result the performance is unacceptably low (chrome cpu usage spikes from 5% to 50% when the effect is triggered).
Could someone help me figure out a way to achieve this effect in a more elegant and less performance-heavy way?
UPDATE:
I have implemented your suggestions: Using translate instead of left, scrolling with translate instead of a js loop, calculations outside of the css tag and using requestAnimationFrame(). The code is nicer and more predictable now, but the animations are still very taxing.
New fiddle
You can try using requestAnimationFrame (it is available in almost all browsers). Seems to make a big difference in Chrome.
JSFiddle
EDIT
Here's a transition-only version, and while it doesn't even implement the jitter, it's useful for comparison. Surprisingly(?) it shows about the same, if not more, CPU usage. (You can check the Profile and Timeline tabs in Chrome)
CSS3 Transition-Only JSFiddle
Here's some information about why that should be expected. Essentially, CSS transitions and requestAnimationFrame work very similarly under the hood.
I would delegate as much as possible to css transitions. So instead of moving the clip with js in the interval callback, transition it from top to bottom (example of transitioning).
You could try something similar with the left property, there's no random easing function but maybe you could achieve something similar with one of the bounce functions. Maybe change the easing function with an interval that's less frequent than your current one.
Also, just by slowing the interval of your current solution you'd get visually ok results with less CPU usage.
Side-note: for a completely different route you can replicate your html in a canvas and apply some effects to that. Google has plenty of results for "canvas glitch".
Update: here's my version of your latest fiddle
I get about 10 % less cpu usage with it when comparing to yours. Key differences are:
uses a timeout instead of requestAnimationFrame. requestAnimationFrame is meant to keep framerate high and the animation smooth but we don't need that for the random offsetting. Timeout is also better than an interval since the loop function is quaranteed to finish before next iteration starts.
removed the transparent background, since transparency has a rendering cost

Javascript Intval Show/Hide Flickering

I'm trying to cycle real fast through a series of images to produce an animation using the below setInterval. However, if I move focus away from the browser tab and then back again, the animation flickers.
I saw a thread about using "real time" to catch the animation up, but since mine uses a simple show/hide, I wasn't sure how that applied.
var animation = setInterval(function(){
var visible = $j('#container img:visible');
var next = visible.next('img');
if(next.length <= 0) next = $j('#container img:first');
next.show();
visible.hide();
},64);
Any thoughts on how to combat the flickering?
Traditionally, JavaScript animation can be handled a number of ways. One approach involves using a timing function such as setTimeout or setInterval to adjust styling every couple milliseconds. Another approach creates a loop that changes styling as many times as possible along the animation’s time frame. The logic of both of these approaches is to throw a large number of animation frames at the browser, so that it hopefully renders smooth-looking motion.
So, even though the browser renders as many frames of the animation as possible, the dropped frames still make for a choppy-looking animation, which is not to mention the performance implications of overloading the processor with as many tasks as possible.
In reality, it’s actually better to render fewer frames, provided they are rendered consistently. That’s because our eyes pick up these subtle variations, and a few dropped frames tend to stick out more than an overall lower frame rate. That’s where HTML5′s requestAnimationFrame API comes in.
Here is an example for your scenario:
function animation(){
var visible = $('#container img:visible');
var next = visible.next('img');
if(next.length <= 0) next = $('#container img:first');
next.show();
visible.hide();
};
setInterval(function() {
window.requestAnimationFrame(animation);
}, 64);
DEMO

Control sprite animation speed

How can I control the animation speed of a sprite using create JS?
When you press right and left, the sprite will run through the frames... I'd like to leave the fps at 60, but alter how fast the frames are looped through without altering the main game FPS rate.
See a demo here. Press right and left arrow to see.
I thought changing the bmp animation frequency property would do the trick...
bmpAnimation.frequency = 2;
But it did not work...
Or should I be using the jQuery animation?
Also, I noticed that each time I hit a random key, the animation plays 1 frame then goes back to first frame. Why is that?
Thanks!
1) The frequency is a property of the spriteSheet-animation, not the bitmapAnimation itself.
So you would have to set the freuqency in the SpriteSheetData itself(http://www.createjs.com/Docs/EaselJS/classes/SpriteSheet.html) or if you want to set it during runtime you could use:
bmpAnimation.spriteSheet.getAnimation("walk_right").frequency = 2;
Please also note that the frequency will be deprecated in the next version of EaselJS in favor of speed (bit this is in the NEXT version, so just something for you to keep in mind)
2) You are currently calling the gotoAndPlay() every frame (if walking), that means that every frame your animation will be set to the first frame of that animation, you could easily avoid this by using something like:
if ( dir == "right" and bmpAnimation.currentAnimation != "walk_right" ) {
bmpAnimation.gotoAndPlay("walk_right");
}
But a better way would be to only call that method ONCE when you start walking and then again when you stop walking.

Categories