Why does javascript execution affect css animation and animated gif in firefox - javascript

There's a big question for me which always comes on when I work on asynchronous loading of data e.g. an image with javascript.
In the past I placed a loader to show the user that something is in progress.
But at the time the data is loading the image won't animate properly. It's bucking all the time but only in Firefox Browser.
Same problem is with css3 animations e.g. a rotation.
Why is that happening?
var imgUrl = 'http://photojournal.jpl.nasa.gov/jpeg/PIA13932.jpg';
var aImagesToLoad = [];
var iLoaded = 0;
var runs = 3;
$(document).on('click', 'a#init-load', function(event) {
event.preventDefault();
if (confirm('A big image is loaded three times now')) {
var i, j;
var self = $(this);
self.fadeOut(500);
$('img').addClass('show');
for ( i = 0; i < runs; i += 1) {
var ts = new Date().getTime();
aImagesToLoad.push(new Image());
aImagesToLoad[aImagesToLoad.length - 1].onload = function(load) {
iLoaded += 1;
if (iLoaded === runs - 1) {
$('img').removeClass('show');
self.fadeIn(500);
}
};
aImagesToLoad[aImagesToLoad.length - 1].src = imgUrl + '?uq=' + (ts + i);
}
}
});
Made a jsfiddle with an ajaxloader image (animated gif) and a css rotation animation. Where it can be tested.

you neeed to add transalte3d(0,0,0) in your stylesheet so it kick in hardware acceleration rendering:
for example try:
transform: scale(1) rotate(359deg) translate3d(0,0,0.1);
the translate3d() property tells the browser to use hardware acceleration for smoother animation
#css-ajaxloader {
transform: scale(1) rotate(0deg) translate3d(0,0,0.1);
}
also i specify the scale at its default so the browser does not inherit any other scale.. since we want the scale at its default value of 1 .. normal size
adding Z to 0.1 will kick in hardware acceleration if the browser supports it
try that

Related

Any way to update the duration of an animation set via element.animate without restarting the animation?

I'm developing a game engine in HTML5. Characters are div elements using an animated sprite for background. As sprite animation have fluid parameters and must be set by code, they can't be predefined in a static CSS definition, thus I use element.animate to set sprite animations to a given row at a given speed knowing my scales and frame counts.
// Applies the given frame and animation to the sprite
// Frame is an angle, clockwise direction: 0 = up, 1 = right, 2 = down, 3 = left
set_animation(frame, duration) {
const scale_x = this.settings.sprite.scale_x * this.settings.sprite.frames_x;
const pos_y = this.settings.sprite.scale_y * -frame;
// Cancel the existing animation
if(this.data_actors_self.anim) {
this.data_actors_self.anim.cancel();
this.data_actors_self.anim = null;
}
// Play the animation for this row or show the first frame if static
if(duration > 0) {
this.data_actors_self.anim = this.element.animate([
{
backgroundPosition: px([0, pos_y])
}, {
backgroundPosition: px([scale_x, pos_y])
}
], {
duration: duration * 1000,
direction: "normal",
easing: "steps(" + this.settings.sprite.frames_x + ")",
iterations: Infinity
});
this.data_actors_self.anim.play();
} else {
this.element.style.backgroundPosition = px([0, pos_y]);
}
}
Obviously that's a snippet from an actor class function: this.element is the div, this.settings is an object with parameters to be used who's names should make sense in this context, the px() function is a simple converter to turn arrays into pixel strings for HTML (eg: [0, 0] to "0px 0px").
The issue I'm having: While I can always run this function to set a new animation, I want the ability to change the speed of the animation without resetting it. It doesn't need to be a smooth transition, for all I care the new speed can be applied at the next iteration... I only want to avoid a visual snap or any kind of reset upon applying the change. Once an animation is set, I have no idea how to access and update its duration parameter. Does anyone have any suggestions?
When using console.log on this.data.anim I'm rightfully told it's an animation object. I tried using JSON.stringify to get more information but nothing relevant is printed. this.data.anim.duration returns undefined so the setting must be stored under some other property. Even if I know that property, I'd like to be sure web browsers will agree with me changing it like this.data.anim.options.duration = new_duration.
You can wait for the end of an iteration before changing the animation duration if that is what is required.
This snippet only sets an event listener for animationiteration event when you click the button to increase the speed.
function upthespeed() {
const div = document.querySelector('div');
div.addEventListener('animationiteration', function() {
div.style.animationDuration = '1s';
});
document.querySelector('button').style.display = 'none';
}
div {
width: 10vmin;
height: 10vmin;
background-color: magenta;
animation: move 10s linear infinite;
}
#keyframes move {
0% {
transform: translateX(50vw);
}
50% {
transform: translateX(0);
}
100% {
transform: translateX(50vw);
}
}
<div></div>
<button onclick="upthespeed()">Click me to increase the speed at the end of the next iteration (you may have to wait!)</button>
The value for the animation duration isn't in the Animation object itself but in the CSS animation-duration property for the Element: so this.data_actors_self.style.animationDuration = new_duration will do the job. It will however restart the animation if it is being played, but if I understand correctly that isn't a problem for you.
Edit: To change the animation's duration without restarting it, all you have to do is set the value of anim.startTime to what it was before. For example:
const startTime = anim.startTime;
this.data_actors_self.style.animationDuration = new_duration
anim.startTime = startTime;

Javascript fade in/out effect freezes when hovering quickly over images

I'm playing around with pure JavaScript, so I created a small fade in/out object, to adjust images opacity onmouseover and onmouseout. Fading works fine when the mouseover and mouseout actions are precise:
Start moving the cursor from the white background
Hover over an image
Hover back over the white background
The problem is, as soon as I start to move the mouse "naturally" from one image to another, the fading (or rather the script itself) freezes.
I'm not sure whether it's a animation-speed problem, or there's something I'm missing in the implementation.
If someone has the time to take a look, I would appreciate a peer check, so I can crack the issue and learn new stuff.
Here's a fiddle: http://jsfiddle.net/6bd3xepe/
Thanks!
As I see it, you have one INTERVAL for you FADER, you need one for each IMG.
My jsfiddle fixes this. I added an ALT-attribute to each IMG with "dome" content, so as to circumvent the jsfiddle working on non-cat-images .. ignore that part - commented out below.
There are some fundamental things wrong with the design - keeping track of objects & references is key. Usage of "this" & "that" aren't helping in the current implementation (see comments to OP). Also, on another note, the usage of "toFixed(2)" is not really required IMHO and you can shorten "o = o + 0.1" to "o += 0.1".
JS:
var fader = {
target: document.getElementsByTagName('img'),
interval: [],
speed: 25,
default_opacity: 1,
init: function() {
this.bindEvents();
},
// Get element's opacity and increase it up to 1
fadeIn: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fI: "+idx+" "+element_opacity);
this.default_opacity = element_opacity.toFixed(2);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) < 1) {
element_opacity = element_opacity + 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
}
}, that.speed);
},
// Get current opacity and decrease it back to the default one
fadeOut: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fO: "+idx+" "+element_opacity);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) > that.default_opacity) {
element_opacity = element_opacity - 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
element.removeAttribute('style');
}
}, that.speed);
},
// Get opacity of an element using computed styles
getOpacity: function(element) {
var styles = window.getComputedStyle(element),
opacity = parseFloat(styles.getPropertyValue('opacity'));
return opacity;
},
bindEvents: function() {
var that = this, count = 0;
for (var i in this.target) {
// the whole "dome" is just a fsfiddle hack - otherwise it sees 7 images instead of 4!
//if( this.target[i].alt == "dome" ){
console.log("COUNT: "+count);
this.target[i].setAttribute('data-idx',count);
this.target[i].onmouseover = function() {
that.fadeIn(this);
}
this.target[i].onmouseout = function() {
that.fadeOut(this);
}
count++;
//}
}
}
};
fader.init();

jQuery .Animate Opacity and .FadeOut/In Both Not Working Inside SetInterval

I trying to make what appears to the user to be an image fader. A string of images fade into each other. All the solutions that I found were complex, and normally required an for every image. I've come up with what should be a simple solution. It's working 90% on Firefox/Chrome/IE11 on Windows. On Android Chrome it's having issues.
Basically my idea is, I have two divs, absolutely positioned, one on top of the other. Both start with a background, sized to cover. The top one fades out, revealing the bottom one, and at the end of the animation, the background-image of the top one (current hidden) is changed to image 3. After a pause, it fades back in, and the background-image of the bottom one is changed to image 4. This repeats indefinitely.
HTML:
<div class="slideshow" id="slideshow-top"></div>
<div class="slideshow" id="slideshow-bottom"></div>
CSS:
.slideshow {
display:block;
background-size:cover;
width: 100%;
height: 100%;
position:absolute;
top:0;
left:0;
}
#slideshow-top {
z-index:-5;
background-image:url(http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg);
}
#slideshow-bottom {
z-index:-10;
background-image:url(http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg);
}
Javascript:
var url_array = [
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-3.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-4.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-5.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-6.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-7.jpg',
'http://www.walldoze.com/images/full/2013/12/04/wallpapers-desktop-winter-nature-x-wallpaper-backgrounds-natureabstract-designs-interesting-hd-19045.jpg'
];
var count = 1;
setInterval(function() {
if (count%2) { // Fade In
jQuery('#slideshow-top').animate({opacity:0}, '200000', function() {
jQuery('#slideshow-top').css('background-image','url('+url_array[count]+')');
});
}
else { //Fade Out
jQuery('#slideshow-top').animate({opacity:1}, '200', function() {
jQuery('#slideshow-bottom').css('background-image','url('+url_array[count]+')');
});
}
count = (count == url_array.length-1 ? 0 : count + 1);
}, 2000);
http://jsfiddle.net/5eXy9/
As seen in the Fiddle above, this mostly works. However, it seems to ignore the length of the animation. Using .fadeOut has the same effect. I've tried going from 200 to 20000, and there doesn't seem to be a difference.
I'm not sure if this is tied into the other issue, which is that on Android (Galaxy S4, Chrome, Android 4.x), the animation doesn't occur at all. It simply changes images. Any ideas?
EDIT: Jan 10 - Timing problem is fixed, but the main issue (Android) is still unsolved. Any thoughts?
The interval keeps going, so when increasing the animation speed, you have increase the interval speed as well.
The way you've built this, you should always keep the speed of both animations equal to the interval, or if you need a delay, increase the interval compared to the animations so it at least has a higher number than the highest number used in the animations.
The reason changing the speed doesn't work at all for you, is because it should be integers, not strings, so you have to do
jQuery('#slideshow-top').animate({opacity:0}, 200000, function() {...
// ^^ no quotes
I would do something like this
var url_array = [
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-1.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-2.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-3.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-4.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-5.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-6.jpg',
'http://www.andymercer.net/wp-content/uploads/2013/12/slider-7.jpg',
'http://www.walldoze.com/images/full/2013/12/04/wallpapers-desktop-winter-nature-x-wallpaper-backgrounds-natureabstract-designs-interesting-hd-19045.jpg'];
var count = 1;
var speed = 2000,
delay = 1000;
$.each(url_array, function(source) { // preload
var img = new Image();
img.src = source;
});
setInterval(function () {
if (count % 2) { // Fade In
jQuery('#slideshow-top').animate({
opacity: 0
}, speed, function () {
jQuery('#slideshow-top').css('background-image', 'url(' + url_array[count] + ')');
});
} else { //Fade Out
jQuery('#slideshow-top').animate({
opacity: 1
}, speed, function () {
jQuery('#slideshow-bottom').css('background-image', 'url(' + url_array[count] + ')');
});
}
count = (count == url_array.length - 1 ? 0 : count + 1);
}, speed + delay);
FIDDLE

Equivalent of #keyframes for jQuery

I'm looking for a way to emulate the CSS #keyframes animations using jQuery.
I need to change the background image each x seconds following a list of images provided when the user moves mouse over an element.
The CSS animations should be:
.readon:hover {
animation: readonin 2s;
}
#keyframes readonin {
0% { background-image: url(1.png); }
50% { background-image: url(2.png); }
100% { background-image: url(3.png); }
}
I've found plugins like Spritely but they works with sprites and I need instead to change the image background of the element.
Use the set-interval function of Javascript seems a bad solution because I can't find a way to stop the animation when the user moves the mouse out of the element.
Use something like...
var images = ["1.png", "2.png", "3.png"];
var $element = $(".readon");
var interval = null;
$element.hover(function () {
var $this = $(this);
var i = 0;
var fn = function () {
$this.css("background-image", "url(" + images[i] + ")");
i = ++i % images.length;
};
interval = setInterval(fn, 666);
fn();
},
function () {
clearInterval(interval);
$(this).css("background-image", "none");
});
jsFiddle of a similar concept (with background colours).
It should be clear enough to see what's going on. Basically we start looping over the images and setting them as the background image on mouse over, and reset it when the mouse leaves.
You can use a library like jQuery-Keyframes
to generate new keyframes at runtime if that is what you are after.

Animation starts moving out of position after some time

I am trying to create a sort of slideshow animation. I have the codes here: jsFiddle.
These tablets would rotate around.
The problem is that, at random times, the animation will move out of line. The wrong tablets undergo wrong animations. Here are the screenshots:
And this is how it looks like when the animations goes wrong
The main problem is I don't understand why the animation would go wrong random times. In my computer it will run properly for hours, but in other cases (especially on Safari).
You could store the expected final css values for each animated el and then in the animate callback set these values, so for each animated el something like
var el = $(selector);
el.data("finalCSS", { your expected final CSS values })
$("selector").animate({animation properties}, function() {
el.css(el.data("finalCSS")).data("finalCSS", undefined);
})
This doesn't help with figuring out why it's happening (but I can't recreate the issue myself), but provides a failsafe to make sure the layout doesn't break;
I believe this happens when you try to animate before the previous animation has ended. Use jQuery stop() just before you animate. For example:
$('#animatingDiv').stop(false, true).animate({height:300}, 200, callback);
The first param(false) will empty the animation queue on that element and the second param(true) will jumps to the end of current animation before starting a new animation.
You can do this with far less code and far fewer headaches.
1. Store your tablet position attributes in classes
.tablet1{
height:100px;
width:140px;
margin-left:-540px;
top: 200px;
z-index:10;
}
2. Use a general function to handle all your transitions.
JQuery UI will do all the work for you if you use switchClass
switchTabletsRight = function(){
var i, next, max = 5;
for(i = 1; i <= max; i++){
next = (i < max)? i + 1 : 1;
$(".tablet" + i).switchClass("tablet" + i, "tablet" + next);
}
};​
Here's the JSfiddle proof of concept: http://jsfiddle.net/nRHag/4/
You are setting CSS positions to decimal values.
img_w = $("#tablet"+num+" img").width();
img_w = img_w *140 / 600;
img_h = $("#tablet"+num+" img").height();
img_h = img_h *140 /600;
...
var new_width = $(this).width() * 140 / 600;
$(this).css('width', new_width);
var new_height = $(this).height() * 140 / 600;
$(this).css('height', new_height);
Your division could be cause decimal results which have different effects in different browsers. Sub pixel CSS positioning may be creating your unintended errors.

Categories