jquery - how to queue up a series of css property changes - javascript

I want to set a series of animation properties - a list of names that are currently invisible but will be marked visible, one right after another, with a set delay of about 100ms between each other. How do I accomplish this with jquery? Essentially, it would be something like this (pseudo-code):
for each $(.item) {
$(this).delay(index*100ms).css({'visibility': 'visible'}
}
The only thing that might compound this, while most of the elements would have the same class, not all of them would, so an animation queue of some sort would work best.

You can use .delay() to do this.
$('#foo').slideUp(300).delay(800).fadeIn(400);
When this statement is executed, the element slides up for 300 milliseconds and then pauses for 800 milliseconds before fading in for 400 milliseconds.
You actually want to do one thing to multiple items, so this is how I'd do that:
$.each($(".a"), function(index, value) {
$(this).delay(index*400).fadeOut(400);
});
You were on the right track with your pseudocode, you need to offset each animation by index * someTime.
Turns out, .delay() actually doesn't work with css() so here's an updated solution:
$.each($(".a"), function(index, value) {
$(this).delay(index*400).queue( function(next){
$(this).css('visibility','visible');
next();
});
});
demo

Related

Interval doesn't clear immediately after repeating for a while

I have a bouncing arrow on my website that I created with Jquery and setInterval, like this:
bouncing = setInterval(function() {
$("div").animate({
top:"30px"
},100,"easeInCubic",function() {
$("div").animate({
top:"0px"
},100,"easeOutCubic");
});
console.log("bounced");
},200);
You can see this in place in a codepen here: http://codepen.io/mcheah/pen/wMmowr
I made it run faster than i needed because its easier to see the issues quicker. My issue is that after leaving the interval running for a few seconds, you'll notice that instead of bouncing back up or down immediately, the bouncing element will stop for half a second and then just hang there, before beginning again. If you leave it running even longer (20 seconds) and then clear the interval, you'll notice that it takes a few seconds to stop bouncing.
My questions are these:
Why does the bouncing go out of sync occasionally?
Why does the clear interval take a while to clear if it's been repeating for a while?
Is there a better way to have a bouncing arrow? Are CSS transitions more reliable?
Thanks for your help!
Your are trying to perfectly coordinate a setInterval() timer and two jQuery animations such that the two come out perfectly coordinated. This is asking for trouble and the two may drift apart over time so it is considered a poor design pattern.
If, instead, you just use the completion of the second animation to restart the first and make your repeat like that, then you have perfect coordination every time.
You can see that here in another version of your codepen: http://codepen.io/anon/pen/NxYeyd
function run() {
var self = $("div");
if (self.data("stop")) return;
self.animate({top:"30px"},100, "easeInCubic")
.animate({top:"0px"}, 100, "easeOutCubic", run);
}
run();
$("div").click(function() {
// toggle animation
var self = $(this);
// invert setting to start/stop
self.data("stop", !self.data("stop"));
run();
console.log("toggled bouncing");
});
It's not a good idea to mix animate() with timers this way. There's NO chance you can synchronize something like this. And there's no need to. You can simply append a function into the animation queue, look here: https://stackoverflow.com/a/11764283/3227403
What animate() does is put an animation request into a job queue which will be processed later, when the right time comes. When you break the interval the stuff that accumulated in the queue will still be processed. There's a method to clear the queue and stop all animation immediately.
The JQuery animation functions actually manipulate CSS, and there is nothing beyond it in HTML. Another option would be using a canvas, but it is a completely different approach and I wouldn't recommend it. With JQuery's animation your already at the best choice.
This is a simple solution to your problem:
function bounce()
{
$("div")
.animate({
top: "30px"
}, 100, "easeInCubic")
.animate({
top: "0px"
}, 100, "easeOutCubic", bounce); // this loops the animation
}
Start bouncing on page load with:
$(bounce);
Stop bouncing on click with:
$("div").click(function() {
$("div").stop().clearQueue().css({ top: "0px" });
// you want to reset the style because it can stop midway
});
EDIT: there were some inaccuracies I corrected now. The running example is on codepen now.
If you want to use javascript for animation you can use something better like the greensock tween library
http://greensock.com/docs/#/HTML5/GSAP/TweenMax/to/
something like this:
var tween = TweenMax.to($("div"), 100, {y: "100px", yoyo: true, repeat: -1});
You could wrap your interval code with:
if(!$("div").is(":animated"))
This will initiate your animation only if your previous one is finished.
The reason why it was bouncing weird is that your animations are queued.
You can check how it works now:
http://codepen.io/luminaxster/pen/XKzLBg
I would recommend using the complete callback when the second animation ends instead and have variable to control a bounce recursive call in this version:
http://codepen.io/luminaxster/pen/qNVzLY

Make sure only one animation is being fired

I am running this animation on $(window).scroll() and it works fine for the most part.
$('#el').animate({ left: toMove }, 500);
The problem is if the user scrolls too fast, two animations will fire and the second animation will fire before the first one has moved far enough. How can I make sure there is only one animation at any given time and then the other animations will follow? I need the element to animate left FULLY any other animations run but I need them all to run. I tried using .queue() but couldn't find the right solution with it.
You could use the stop function like this to smooth things up:
$('#el').stop(true, false).animate({ left: toMove }, 500);
The stop function, when calling like that will clear the animation queue for any waiting animation, as well as stop the current animation if any.
Edit
If you want the animation to end before animating again you just need to call .stop(true, true) instead

Is it okay to use .animate with 0 duration all the time? Is there a better alternative?

I've grown used to code my jquery animation by doing the following.
Set the initial state of the element (things like width, height, top, left, opacity, etc...) using either css or javascript. This initial state is the state at the end of the animation.
Use .animate with 0 duration to move the element to the state in which elements are at the beginning of the animation.
Use a regular .animate with the appropriate duration to do the animation in which at the end, the elements are back to state in step 1.
Here is an example.
/* css file */
.animatedObject
{
width:60%;
}
// javascript file
$('.animatedObject')
.animate({width: '-=20%'}, 0)
.animate({width: '+=20%'}, 1000);
There are several advantage using this code at least for me. First, it looks clear to me what I'm trying to animate. Second, if javascript is disabled, the object would be at the end state of the animation which is often where I want it to be for graceful degradation. Third, objects can change position slightly for adjustments using css and the animation would still look largely the same.
The reason I'm not using the .css is that it if I try to replace the animate with 0 duration with the .css method, I would get differing animation starting point. I don't think it support += or -=, or if it does, it behaves differently.
So, is this a good way to do this? What is the industry standard way?
Well you could still use .css():
var anObj = $('.animatedObject');
anObj.css("width", anObj.css("width") - (20 / anObj.css("width"))).animate({
width: '+=20%'
}, 1000);
I believe this would work faster.
Edit:
I did a little benchmark for you, using jsperf.com. Here are my results (using Google Chrome):
.animate({}, 0)
Code:
$('.animatedObject')
.animate({width: '-=20%'}, 0)
.animate({width: '+=20%'}, 1000);
End Results:
10,013 operations per second
±7.48%
fastest
.css();
Code:
var anObj = $('.animatedObject');
anObj.css("width", anObj.css("width") - (20 / anObj.css("width"))).animate({
width: '+=20%'
}, 1000);
End Results:
2,477 operations per second
±6.39%
75% slower
Conclusion
Keep the animation function. It turns out using .css() is actually slower. I guess it's the fact that you have 2 extra functions. It's not worth it. This was actually a surprise to me as I thought .css() would function faster than it did. Test this yourself in your browser!

jQuery Queue Functions

Take these two methods:
function moveUp() {
this.element.rotate(0);
this.element.animate({"margin-top": "-=51px"}, "slow");
//do some more things for instance.
}
function moveRight() {
this.element.rotate(90);
this.element.animate({"margin-left": "+=51px"}, "slow");
}
Just in case, I rotate an object, then move it around with an animation. These functions are mapped to key presses, so if the user presses Up or Right, the object moves. My problem is that the animations are queued properly, because jQuery uses the fx queue for them. But the rotation happens inmediately, as well as any other thing I do, since they are custom things I have in my code.
The consequence is obvious, if you press say up and immediately press right, the object rotates up, but while it is moving upwards, it rotates right, it doesn't really wait until it gets there.
How can I write this code so that the whole methods are chained and not just their animations. I could add callbacks, but they won't always execute together, I need them to be queued if they happen to be called simultaneously, but I don't quite understand how to use the queue functionality in jQuery.
By the way, to see more of the code behind this, I have recently asked this question also: Array of prototype functions
You could animate it using something like:
$elem.animate({rotation: 90},
{
duration: 'slow',
step: function(now, fx) {
$(this).css({ "transform": "rotate("+now+"deg)", "-webkit-transform": "rotate("+now+"deg)", "-moz-transform": "rotate("+now+"deg)" });
}
})
That would add it to the queue automatically
I've made a jsfiddle here:
http://jsfiddle.net/obartra/7Qwgd/1/
EDIT: Just to explain my rationale, I'm guessing that the rotate method you are using is using setTimeout to set the rotation in the CSS progressively, thus not adding it to the animation queue. The code here uses animate to rotate the element so it gets added to the animation queue.

how to execute multiple animations simultaneously in mootools?

I have two divs (say, divs with ids: div1 and div2) whose height I need to expand using Fx.Tween
And I want the animations to be simultaneous.
'#div1' 's style.height is to be changed from auto to 0.
'#div2' 's style.height is to be changed for current height to a new height calculated by a function called calculateHeight()
how do I do that?
Mootools animations aren't blocking (animations in JS very rarely are!) so simply executing the two tweens sequentially will have the desired affect (as close as a human can perceive)
function go()
{
$('div1').tween('height', 0);
$('div2').tween('height', calculateHeight());
}
function calculateHeight()
{
return 0; //or whatever
}
I think this has to do with the wait:false option.
I'm not a programmer and mootools is easy but not as much to be so good at it but I remember reading some docs where it says you can control wether the second animation works as soon as the first one ends or simultaneously.
Chain Method: wait
Injects pauses between chained events.
Syntax
myClass.wait(duration);
Arguments
1. duration - (integer) The duration (in milliseconds) to pause the chain stack; defaults to 500.
I think you should CHAIN the morphing and make it wait(0). But I'm not sure. Hope this helps.

Categories