Randomizing CSS3 animation times with javascript/jquery? - javascript

I'd like to randomize the duration of a given animation every page refresh. I've been trying it with jquery, but it doesn't seem to be working: I have that doesn't seem to be working.
Relevant css:
#background1 {
-webkit-animation: background1 10s;
}
Animation background1 is defined elsewhere; I'm not trying to change the keyframe animation part.
Jquery:
var bg1 = $('#background1');
var number = Math.floor(Math.random() * 10) + 5;
bg1.css('-webkit-animation', 'background1 ' + number + 's');
Nothing seems to happen. Help out a jquery noob?

When the browser registers your CSS declaration it registers the name of the animation background1 and the duration of 10s and then starts the animation immediately. If you want to restart the animation with a new duration you must wait till the animation finishes, or you must attach a different animation name with the new duration.
You can test at this fiddle: http://jsfiddle.net/mfdj/Phnf5/9/
You can always immediately trigger an animation when you change the name
You can restart the same animation by changing just the duration only if you set a different value for the duration and do so after the the current animation has finished
Since your initial animation is 10s long and you want to immediately trigger a duration with a randomized time you should simply remove this declaration:
#background1 {
-webkit-animation: background1 10s;
}
and you should get the result you want.
Please note that there are some quirks in the way browsers handle reattaching/retriggering css animations with the same name but different durations. For instance, in the example fiddle Chrome handles the shake and flash animations differently. Try starting a 10second animation and then interrupt with a 1second animation of the same name. The shake will finish it's 10s while the flash simply clears the animation. However, if you switch from flash to shake (and vice versa) the animation always starts fresh. These are the quirks of triggering animation with css that you must be aware of.

What other answers said is wrong!....That css is fine, it does what the answer above me said but is overwritten by jquery. In fact, your jquery is almost fine as well too.
You just get the animation function in css wrong.
You are never defining the animation to run. You are attaching animation to element using jquery successfully. But what animation.
Take a look at my example: http://jsfiddle.net/techsin/Lju95/1/
To learn more about it go here: http://css-tricks.com/snippets/css/keyframe-animation-syntax/
In bg1.css('-webkit-animation', 'background1 ' + number + 's'); background1 should refer to animation name not the element name which you are already referring to by bg1, aren't you?
Animation name and animation itself can be defined in css, and then be called with new random value.
To learn jquery easily go to here: https://tutsplus.com/course/30-days-to-learn-jquery/
To learn Html/Css3: go to w3schools.com then http://learncss.tutsplus.com/

As Fox pointed out - remove that declaration. Then, with your JS you can do something like:
var bg1 = $('#background1');
var number = Math.floor(Math.random() * 10) + 5;
bg1.css('-webkit-animation', 'background1 ' + number + 's');
You could also put it in a class to keep things cleaner (kinda)
#background1 .animated {
-webkit-animation-name: background1;
}
// jQ
$('#background1').addClass('animated').css(
'-webkit-animation-duration',
(Math.floor(Math.random() * 10) + 5) + 's'
);

Related

How to apply animations on two elements at once with animated.css

I am working on a simple slideshow where each slide has its own duration.
I would like to add transitions between the slides using animate.css by adding and removing classes on the current and the next slides.
My problem is that - with my current approach - only the next slide will be animated (it slides in) but the current one is just disappear without any animation.
I have tried to detect the end of the current animation and then change(adding/removing) the classes but in that case there was a huge gap between the slides...
How can make sure that two animations plays at once?`
var slides = $this.find('.slide');
slideIt = function(time) {
setTimeout(function() {
var duration = slides.eq(nextSlide).data('duration');
slides.eq(currentSlide)
.removeClass(animateIn)
.addClass(animateOut)
.hide();
slides.eq(nextSlide)
.removeClass(animateOut)
.addClass(animateIn)
.show();
slideIt(duration);
currentSlide = nextSlide; nextSlide ++;
if (nextSlide == slidesLength) { nextSlide = 0;}
},time);
};
Thank you for the help in advance!
Ps.: While typing this post I have realized that it must be the .hide() but without it only the first slide displayed.
Native CSS animations on two different elements should always run at the same time.
But if you hide an element, it disappears before the animation has even started. You have to add a timer for that.
slides.eq(currentSlide)
.removeClass(animateIn)
.addClass(animateOut);
// save the jQuery instance of the element that should be hidden.
var $currentSlide = slides.eq(currentSlide);
// hide this element later
setTimeout(function() {
$currentSlide.hide();
}, 1000); // 1000 for 1000ms, replace that with the duration of the animateOut animation
If my first answer doesn't satisfy you, because you want so solve that on the CSS side, when there is a second way:
Remove the .hide() in JavaScript.
Make sure your CSS animation ends with a state, there the element cannot be seen anymore (like transform: scale(0) or left: -100%).
Maintain that final state of the CSS animation. To do that, see: Maintaining the final state at end of a CSS3 animation

Different behavior of transition when launched via event listener vs direct function call [duplicate]

I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.
Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.
Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.
Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?
I've put together a little example of what the code is right now: http://g2f.nl/38rvma
Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/
To make transition work, three things have to happen.
the element has to have the property explicitly defined, in this case: opacity: 0;
the element must have the transition defined: transition: opacity 2s;
the new property must be set: opacity: 1
If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:
window.setTimeout(function() {
slides[targetIndex].className += " target-fadein";
}, 100);
Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.
Adding transition to an element is not what triggers the animation, changing the property does.
// Works
document.getElementById('fade1').className += ' fade-in'
// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'
// Works
document.getElementById('fade3').className = 'fadeable'
window.setTimeout(function() {
document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
opacity: 0;
}
.fade-in {
opacity: 1;
transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>
Trick the layout engine!
function finalizeAndCleanUp (event) {
if (event.propertyName == 'opacity') {
this.style.opacity = '0'
this.removeEventListener('transitionend', finalizeAndCleanUp)
}
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'
As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.
Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.
Cleanup code added for completeness.
Some people have asked about why there is a delay. The standard wants to allow multiple transitions, known as a style change event, to happen at once (such as an element fading in at the same time it rotates into view). Unfortunately it does not define an explicit way to group which transitions you want to occur at the same time. Instead it lets the browsers arbitrarily choose which transitions occur at the same time by how far apart they are called. Most browsers seem to use their refresh rate to define this time.
Here is the standard if you want more details:
http://dev.w3.org/csswg/css-transitions/#starting

CSS animation won't trigger on addClass in jQuery

I have a very strange issue. I'm loading articles from JSON in jQuery and as they load, I'd like to add a class of 'animate' to each dynamic element.
$.each(jsonArticles, function (i, article) {
var $articleHTML = $(
'<article class="article">' +
'<img src="' + jsonObject.imagePath + article.reviewImage + '" alt="">' +
'<h1>' + article.reviewTitle + '</h1>' +
'<p>' + article.reviewSummary + '</p>' +
'</article>');
$articles
.append($articleHTML)
.find("article")
.addClass("animate");
});
All of this works great and checking in Firebug reveals that the class is successfully added to each article tag.
However, when trying to use a CSS transition on the article for the class that's added, it does not animate, but instead skips straight to the final style (opacity: 1).
.article {
opacity: 0;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.article.animate {
opacity: 1;
}
The animation doesn't happen, but the class is added and the article is successfully set to opacity: 1. It shows up instantly.
Anyone have any ideas about this? I cannot figure this one out at all.
On another point, which is rather interesting...if I change the .animate class to have a :hover, then the articles won't show until I hover and the animation does work. Why it would work for hover and not when it's simply added immediately, seems strange to me.
.article.animate:hover {
opacity: 1;
}
I'd appreciate any input.
Thanks,
Mikey.
Live Demo: http://jsfiddle.net/Pz5CD/
Notice how the articles just pop in at 100% opacity. No animation is seen.
Update:
It turns out the OP wants to fade in each element sequentially, which is beyond the scope of the original question. I'll leave my answer here as an answer to the original question.
CSS animation won't trigger on addClass in jQuery
The issue is that your new html is added to the page and the animate class is added before the css for that html has been applied. The browser will skip ahead like that for the sake of efficiency. For example, if you added a class, then removed it, and repeated that process a hundred times, there wouldn't be a visual difference. It would have just skipped to the result. For this reason, you have to force a redraw on the element so that all previous styles have applied before adding the class. I wrote a function to handle this that should work in every circumstance on every browser, though there's no way to guarantee the behavior of a reDraw. It probably will always work and it's nice to have!
Live demo here (click). You can tell the reDraw is making the difference by commenting it out and just leaving the addClass().
$('body').append($articleHTML);
$a = $('body').find("article");
reDraw($a).then(function() {
$a.addClass("animate");
});
function reDraw($element) {
var deferred = new $.Deferred();
setTimeout(function() {
var h = $element[0].offsetHeight;
var s = $element[0].getComputedStyle;
deferred.resolve();
},0);
return deferred.promise();
}
The best way to force a redraw is to either access the offsetHeight or getComputedStyle of an element. However, there have been cases where those have failed for force a redraw on certain mobile devices. To add some extra encouragement for a redraw, I added a setTimeout as well. Even a time of 0 on the timeout will work, but it throws off the call stack, so I use a promise to ensure the next operation (adding your class) will happen after the redraw. That just means you'll use the syntax I demonstrated above to add the class - redraw($element).then(function() { //your code
For fun, I made a little demo of flipping classes with and without reDraw. http://jsbin.com/EjArIrik/1/edit
You need to add the class to the element after it is rendered to the dom, a set timout might work
setTimeout(function(){
$articleHTML.addClass("animate");
}, i * 500 );
http://jsfiddle.net/Pz5CD/1/

JavaScript/CSS: Delay between adding element to DOM and its CSS rules being applied?

I am using JavaScript to dynamically add an element to the DOM. I want to use CSS3 transitions to "fade in" the element as it is added.
I am using something like the following to achieve this:
function add(el) {
el.className += ' fader';
el.style.opacity = 0;
document.getElementById('parent-element').appendChild(el);
//setTimeout(function () { el.style.opacity = 1; }, 5);
el.style.opacity = 1;
}
And the CSS:
.fader {
-webkit-transition: opacity 0.5s;
}
This does not work as expected - the element does not fade in. If I replace the line el.style.opacity = 1; with setTimeout(function () { el.style.opacity = 1; }, 5);, as seen commented-out above, it does work as expected.
I am guessing that the first case does not work as there is some delay between adding the element and the appropriate CSS rules being applied to it. The 5ms delay created by the setTimeout in the second case gives enough time for these rules to be applied, therefore the fade takes place as expected.
Firstly, is this a correct assumption? Secondly, is there a better way to solve this? The setTimout feels like a hack. Is there perhaps some event that is fired once the element has had all its styles applied?
For a CSS3 transition to work, the object has to exist in a particular state and then you have to make a change to the object that triggers the transition.
For a variety of reasons, all of my experience with CSS3 transitions has shown me that a state that counts for this is only a state that it exists in when your javascript returns and the browser goes back to its event loop. It's as if, the only way you can tell the browser to loop at your object now and remember it's state for future transitions is to go back to the browser event loop. There are some programming reasons why this may be the case (so it's not trying to execute transitions as you're programmatically building your object and changing it), but those issues could have been solved a different way (like with a specific method call to codify the object now), but it wasn't done that way.
As such, your solution is the way I've found to do it. Create the object in it's initial state. Set a timer for a very short duration. Return from all your javascript so the object will get codified in its initial state and so the timer can fire. In the timer event, add a class to the object that triggers the CSS3 transition.
I don't honestly know if CSS3 transitions are specified this way in the specification, but my experience in Safari, Firefox and Chrome has been that this is how they work.

animating addClass/removeClass with jQuery

I am using jQuery and jQuery-ui and want to animate various attributes on various objects.
For the sake of explaining the issue here I've simplified it to one div that changes from blue to red when the user mouses over it.
I am able to get the behavior I want when using animate(), however when doing so the styles I am animating have to be in the animation code and so are separate from my style sheet. (see example 1)
An alternative is using addClass() and removeClass() but I have not been able to re-create the exact behavior that I can get with animate(). (see example 2)
Example 1
Let's take a look at the code I have with animate():
$('#someDiv')
.mouseover(function(){
$(this).stop().animate( {backgroundColor:'blue'}, {duration:500});
})
.mouseout(function(){
$(this).stop().animate( {backgroundColor:'red'}, {duration:500});
});
it displays all the behaviors I am looking for:
Animates smoothly between red and blue.
No animation 'overqueue-ing' when the user moves their mouse quickly in and out of the div.
If the user moves their mouse out/in while the animation is still playing it eases correctly between the current 'halfway' state and the new 'goal' state.
But since the style changes are defined in animate() I have to change the style values there, and can't just have it point to my stylesheet. This 'fragmenting' of where styles are defined is something that really bothers me.
Example 2
Here is my current best attempt using addClass() and removeClass (note that for the animation to work you need jQuery-ui):
//assume classes 'red' and 'blue' are defined
$('#someDiv')
.addClass('blue')
.mouseover(function(){
$(this).stop(true,false).removeAttr('style').addClass('red', {duration:500});
})
.mouseout(function(){
$(this).stop(true,false).removeAttr('style').removeClass('red', {duration:500});
});
This exhibits both property 1. and 2. of my original requirements, however 3 does not work.
I understand the reason for this:
When animating addClass() and removeClass() jQuery adds a temporary style to the element, and then increments the appropriate values until they reach the values of the provided class, and only then does it actually add/remove the class.
Because of this I have to remove the style attribute, otherwise if the animation is stopped halfway the style attribute would remain and would permanently overwrite any class values, since style attributes in a tag have higher importance than class styles.
However when the animation is halfway done it hasn't yet added the new class, and so with this solution the color jumps to the previous color when the user moves their mouse before the animation is completed.
What I want ideally is to be able to do something like this:
$('#someDiv')
.mouseover(function(){
$(this).stop().animate( getClassContent('blue'), {duration:500});
})
.mouseout(function(){
$(this).stop().animate( getClassContent('red'), {duration:500});
});
Where getClassContent would just return the contents of the provided class. The key point is that this way I don't have to keep my style definitions all over the place, but can keep them in classes in my stylesheet.
Since you are not worried about IE, why not just use css transitions to provide the animation and jQuery to change the classes. Live example: http://jsfiddle.net/tw16/JfK6N/
#someDiv{
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
Another solution (but it requires jQueryUI as pointed out by Richard Neil Ilagan in comments) :-
addClass, removeClass and toggleClass also accepts a second argument; the time duration to go from one state to the other.
$(this).addClass('abc',1000);
See jsfiddle:- http://jsfiddle.net/6hvZT/1/
You could use jquery ui's switchClass, Heres an example:
$( "selector" ).switchClass( "oldClass", "newClass", 1000, "easeInOutQuad" );
Or see this jsfiddle.
You just need the jQuery UI effects-core (13KB), to enable the duration of the adding (just like Omar Tariq it pointed out)
I was looking into this but wanted to have a different transition rate for in and out.
This is what I ended up doing:
//css
.addedClass {
background: #5eb4fc;
}
// js
function setParentTransition(id, prop, delay, style, callback) {
$(id).css({'-webkit-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'-moz-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'-o-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'transition' : prop + ' ' + delay + ' ' + style});
callback();
}
setParentTransition(id, 'background', '0s', 'ease', function() {
$('#elementID').addClass('addedClass');
});
setTimeout(function() {
setParentTransition(id, 'background', '2s', 'ease', function() {
$('#elementID').removeClass('addedClass');
});
});
This instantly turns the background color to #5eb4fc and then slowly fades back to normal over 2 seconds.
Here's a fiddle
Although, the question is fairly old, I'm adding info not present in other answers.
The OP is using stop() to stop the current animation as soon as the event completes. However, using the right mix of parameters with the function should help. eg. stop(true,true) or stop(true,false) as this affects the queued animations well.
The following link illustrates a demo that shows the different parameters available with stop() and how they differ from finish().
http://api.jquery.com/finish/
Although the OP had no issues using JqueryUI, this is for other users who may come across similar scenarios but cannot use JqueryUI/need to support IE7 and 8 too.

Categories