In react native I have these two lines of code:
this._animateContent('contentPos', -700);
this._callOnSwipe();
I want to wait for the first line to finish before executing the second line. Is there a way to do this?
Here is the function
_animateContent: function _animateContent(state, endValue) {
this.tweenState(state, {
easing: _reactTweenState2.default.easingTypes.easeInOutQuad,
duration: 400,
endValue: -600
});
},
Look at the documentation for react-tween-state:
onEnd: the callback to trigger when the animation's done.
Put your code in an onEnd function.
I think you need tweenState, to give you a callback or promise in the end, so that you can set the callback as this._callOnSwipe().
not sure what tweenState means,
but I guess it would be https://github.com/chenglou/react-tween-state this one, as I just searched from Google.
And it supports onEnd attribute for callback and the example code looks like this.
_animateContent: function _animateContent(state, endValue, callback) {
this.tweenState(state, {
easing: _reactTweenState2.default.easingTypes.easeInOutQuad,
duration: 400,
endValue: -600,
onEnd: callback
});
},
and you can then call
var self = this;
this._animateContent('contentPos', -700, function() {
self._callOnSwipe()
});
Hope it helps.
From this React Tween State documentation there was a configuration of onEnd: endCallback that you can use to run the _callOnSwipe function.
var this_parent = this;
this._animateContent('contentPos', -700 , function(){this_parent._callOnSwipe()});
_animateContent: function _animateContent(state, endValue, EndFunction) {
this.tweenState(state, {
easing: _reactTweenState2.default.easingTypes.easeInOutQuad,
duration: 400,
endValue: -600,
onEnd: EndFunction
});
},
Related
I thought it would be simple but I still can't get it to work. By clicking one button, I want several animations to happen - one after the other - but now all the animations are happening at once. Here's my code - can someone please tell me where I'm going wrong?:
$(".button").click(function(){
$("#header").animate({top: "-50"}, "slow")
$("#something").animate({height: "hide"}, "slow")
$("ul#menu").animate({top: "20", left: "0"}, "slow")
$(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
Queue only works if your animating the same element. Lord knows why the above got voted up but it will not work.
You will need to use the animation callback. You can pass in a function as the last param to the animate function and it will get called after the animation has completed. However if you have multiple nested animations with callbacks the script will get pretty unreadable.
I suggest the following plugin which re-writes the native jQuery animate function and allows you to specify a queue name. All animations that you add with the same queue name will be run sequentially as demonstrated here.
Example script
$("#1").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
$("#2").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
$("#3").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
I know this is an old question, but it should be updated with an answer for newer jQuery versions (1.5 and up):
Using the $.when function you can write this helper:
function queue(start) {
var rest = [].splice.call(arguments, 1),
promise = $.Deferred();
if (start) {
$.when(start()).then(function () {
queue.apply(window, rest);
});
} else {
promise.resolve();
}
return promise;
}
Then you can call it like this:
queue(function () {
return $("#header").animate({top: "-50"}, "slow");
}, function () {
return $("#something").animate({height: "hide"}, "slow");
}, function () {
return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}, function () {
return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
You could do a bunch of callbacks.
$(".button").click(function(){
$("#header").animate({top: "-50"}, "slow", function() {
$("#something").animate({height: "hide"}, "slow", function() {
$("ul#menu").animate({top: "20", left: "0"}, "slow", function() {
$(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
});
});
});
A slight improvement on #schmunk's answer is to use a plain object jQuery object's queue in order to avoid conflicting with other unrelated animations:
$({})
.queue(function (next) {
elm1.fadeOut('fast', next);
})
.queue(function (next) {
elm2.fadeIn('fast', next);
})
// ...
One thing to keep in mind is that, although I have never run into problems doing this, according to the docs using the queue methods on a plain object wrapper is not officially supported.
Working With Plain Objects
At present, the only operations supported on plain JavaScript objects wrapped in jQuery
are: .data(),.prop(),.bind(), .unbind(), .trigger() and .triggerHandler().
You can also put your effects into the same queue, i.e. the queue of the BODY element.
$('.images IMG').ready(
function(){
$('BODY').queue(
function(){
$('.images').fadeTo('normal',1,function(){$('BODY').dequeue()});
}
);
}
);
Make sure you call dequeue() within the last effect callback.
Extending on jammus' answer, this is perhaps a bit more practical for long sequences of animations. Send a list, animate each in turn, recursively calling animate again with a reduced list. Execute a callback when all finished.
The list here is of selected elements, but it could be a list of more complex objects holding different animation parameters per animation.
Here is a fiddle
$(document).ready(function () {
animate([$('#one'), $('#two'), $('#three')], finished);
});
function finished() {
console.log('Finished');
}
function animate(list, callback) {
if (list.length === 0) {
callback();
return;
}
$el = list.shift();
$el.animate({left: '+=200'}, 1000, function () {
animate(list, callback);
});
}
Animate Multiple Tags Sequentially
You can leverage jQuery's built-in animation queueing, if you just select a tag like body to do global queueing:
// Convenience object to ease global animation queueing
$.globalQueue = {
queue: function(anim) {
$('body')
.queue(function(dequeue) {
anim()
.queue(function(innerDequeue) {
dequeue();
innerDequeue();
});
});
return this;
}
};
// Animation that coordinates multiple tags
$(".button").click(function() {
$.globalQueue
.queue(function() {
return $("#header").animate({top: "-50"}, "slow");
}).queue(function() {
return $("#something").animate({height: "hide"}, "slow");
}).queue(function() {
return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}).queue(function() {
return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
});
http://jsfiddle.net/b9chris/wjpL31o0/
So, here's why this works, and what it's doing:
The call to $.globalQueue.queue() is just queueing a call to your tag's animation, but it queues it on the body tag.
When jQuery hits your tag animation in the body queue, your tag's animation starts, on the queue for your tag - but the way the jQuery animation framework works, any custom animation callback causes a tag's animation queue (the body's in this case) to halt, until the custom animation calls the passed-in dequeue() function. So, even though the queues for your animated tag and body are separate, the body tag's queue is now waiting for its dequeue() to be called. http://api.jquery.com/queue/#queue-queueName-callback
We just make the last queued item on the tag's queue a call to continue the global queue by calling its dequeue() function - that's what ties the queues together.
For convenience the globalQueue.queue method returns a this reference for easy chaining.
setInterval
For the sake of completeness, it's easy to land here just seeking an alternative to setInterval - that is you're not so much looking to make separate animations coordinate, as just fire them over time without the strange surge ahead in your animation caused by the way newer browsers will postpone animation queues and timers to save CPU.
You can replace a call to setInterval like this:
setInterval(doAthing, 8000);
With this:
/**
* Alternative to window.setInterval(), that plays nicely with modern animation and CPU suspends
*/
$.setInterval = function (fn, interval) {
var body = $('body');
var queueInterval = function () {
body
.delay(interval)
.queue(function(dequeue) {
fn();
queueInterval();
dequeue(); // Required for the jQuery animation queue to work (tells it to continue animating)
});
};
queueInterval();
};
$.setInterval(doAthing, 8000);
http://jsfiddle.net/b9chris/h156wgg6/
And avoid those awkward blasts of animation when a background tab has its animations re-enabled by the browser.
This has already been answered well (I think jammus's answer is the best) but I thought I'd provide another option based on how I do this on my website, using the delay() function...
$(".button").click(function(){
$("#header").animate({top: "-50"}, 1000)
$("#something").delay(1000).animate({height: "hide"}, 1000)
$("ul#menu").delay(2000).animate({top: "20", left: "0"}, 1000)
$(".trigger").delay(3000).animate({height: "show", top: "110", left: "0"}, "slow");
});
(replace 1000 with your desired animation speed. the idea is your delay function delays by that amount and accumulates the delay in each element's animation, so if your animations were each 500 miliseconds your delay values would be 500, 1000, 1500)
edit: FYI jquery's 'slow' speed is also 600miliseconds. so if you wanted to use 'slow' still in your animations just use these values in each subsequent call to the delay function - 600, 1200, 1800
I was thinking about a backtracking solution.
Maybe, you can define that every object here has the same class, for example .transparent
Then you can make a function, say startShowing, that looks for the first element which has the .transparent class, animate it, remove .transparent and then call itself.
I can't assure the sequence but usually follows the order in which the document was written.
This is a function I did to try it out
function startShowing(){
$('.pattern-board.transparent:first').animate(
{ opacity: 1},
1000,
function(){
$(this).removeClass('transparent');
startShowing();
}
);
}
Use the queue option:
$(".button").click(function(){
$("#header").animate({top: "-50"}, { queue: true, duration: "slow" })
$("#something").animate({height: "hide"}, { queue: true, duration: "slow" })
$("ul#menu").animate({top: "20", left: "0"}, { queue: true, duration: "slow" })
$(".trigger").animate({height: "show", top: "110", left: "0"}, { queue: true, duration: "slow" });
});
This is more a question involving why one of my solutions works and the other doesn't. I'm fairly new to JS, only been learning a couple of months and whilst I have most of the basics down, I feel my knowledge of best practice is lacking.
I'm creating an animated hero image for the top of an infographic and on it I'm using JS to create two trains moving across the screen, one from left to right and the other right to left. I created the following code, which worked:
$(document).ready(function() {
var anim = {
train1: $(".train-one"),
train2: $(".train-two"),
moveLeft: function(percent, duration) {
anim.train1.animate({left: percent}, duration, "linear")
},
moveRight: function(percent, duration) {
anim.train2.animate({right: percent}, duration, "linear")
},
leftTrain: function() {
anim.moveLeft("33%", 1000, anim.moveLeft)
anim.moveLeft("66%", 2000, anim.moveLeft)
anim.moveLeft("100%", 1000, anim.moveLeft)
anim.moveLeft("-100px", 1)
},
rightTrain: function() {
anim.moveRight("40%", 1000, anim.moveRight)
anim.moveRight("60%", 2000, anim.moveRight)
anim.moveRight("100%", 1000, anim.moveRight)
anim.moveRight("-100px", 1)
},
};
anim.leftTrain();
anim.rightTrain();
setInterval(anim.leftTrain, 5000);
setInterval(anim.rightTrain, 6000);
});
What I'm wondering is why the following didn't work when I expected it to, I created a seperate method to reset the train once all the callbacks had been completed:
resetLeftTrain: function(test) {
anim.train1.css("left", "-100px ");
},
leftTrain: function() {
anim.moveLeft("33%", 1000, anim.moveLeft)
anim.moveLeft("66%", 2000, anim.moveLeft)
anim.moveLeft("100%", 1000, anim.resetLeftTrain)
anim.resetLeftTrain()
},
Sorry if the answer is really obvious, I'm not so used to callbacks in plain JS. Please feel free to give any other pointers regarding structure etc. Really appreciate it!
Cheers.
EDIT: I solved the issues thanks to the answers below and the code now works perfectly as follows:
$(document).ready(function() {
var anim = {
train1: $(".train-one"),
train2: $(".train-two"),
moveLeft: function(percent, duration, callback) {
this.train1.animate({left: percent}, duration, "linear", callback)
},
moveRight: function(percent, duration, callback) {
this.train2.animate({right: percent}, duration, "linear", callback)
},
resetLeftTrain: function() {
this.train1.css("left", "-100px");
},
resetRightTrain: function() {
this.train1.css("right", "-100px");
},
leftTrain: function() {
var self = this;
this.moveLeft("33%", 1000, function() {
self.moveLeft("66%", 2000, function(){
self.moveLeft("100%", 1000, function(){
self.resetLeftTrain();
});
});
});
},
rightTrain: function() {
var self = this;
this.moveRight("40%", 1000, function() {
self.moveRight("60%", 2000, function(){
self.moveRight("100%", 1000, function(){
self.resetRightTrain();;
});
});
});
},
};
anim.leftTrain();
anim.rightTrain();
setInterval($.proxy(anim.leftTrain, anim), 5000);
setInterval($.proxy(anim.rightTrain, anim), 6000);
});
Next time I may look into using the Jquery .promise() method to avoid too much ugly indentation.
Thanks for all the help, hope the question and it's answers are useful to others
You need to provide anonymous callback functions to the animations. Your lack or semi-colons give the impression they are nested :)
anim.moveRight("40%", 1000, function(){
anim.moveRight("60%", 2000, function(){
anim.moveRight("100%", 1000, function(){
anim.moveRight("-100px", 1);
});
});
});
and make your methods take a callback to pass on:
moveRight: function(percent, duration, callback) {
anim.train2.animate({right: percent}, duration, "linear", callback);
},
The end result is that as each animate call finished, it will execute the code provided, effectively chaining the animations together.
Year I also needed a few weeks to understand the "callback"-concept right ;-)
Here are an example (only for your left train):
$(document).ready(function() {
var anim = {
train1: $(".train-one"),
moveLeft: function(percent, duration, cb) {
anim.train1.animate({left: percent}, duration, cb);
},
leftTrain: function() {
anim.moveLeft("33%", 1000, function() {
anim.moveLeft("66%", 2000, function(){
anim.moveLeft("100%", 1000, function(){
anim.moveLeft("-100px", 1);
});
});
});
},
};
anim.leftTrain();
setInterval(anim.leftTrain, 5000);
});
Things you should look at:
You need to add callback-calls (here as a param) to your functions
You missed a few ";"
If you have trouble with all the "callback-hell" look for "promises" (jQuery has a build-in function for that) or "async.js" <- i really love that
I know there are many questions where this jQuery Error was the problem. But how you may see, this error isn't very helpful at all for solving the problem. I work with jQuery 1.10.2 and have a plugin at version 1.3 called jRumble included.
Now the error comes with this script:
jQuery(document).ready(function() {
jQuery('.landing-bar').jrumble({
x: 1,
y: 1,
rotation: 0
});
var rumbleStart = function() {
jQuery('.landing-bar').trigger('startRumble');
setTimeout(rumbleStop, 200);
};
var rumbleStop = function() {
jQuery('.landing-bar').trigger('stopRumble');
setTimeout(rumbleStart, 785);
};
rumbleStart();
animateScroll();
});
function animateScroll() {
jQuery('.landing-bar').animate({
width: '100%'
}, {
duration: 30000,
easing: 'linear',
complete:function() {
jQuery(this).css("width","0%");
}
});
animateScroll();
}
What is wrong with my code? I think it could be, that a syntax is wrong for jQuery 1.10..
Thanks for any help!
Put animateScoll() in your complete callback. You don't want it to be called over and over again like that.
function animateScroll() {
jQuery('.landing-bar').animate({
width: '100%'
}, {
duration: 30000,
easing: 'linear',
complete:function() {
jQuery(this).css("width","0%");
animateScroll();
}
});
}
EXPLANATION:
the jQuery callback complete is called when your animating has finished. What you are essentially doing is calling the animate function over and over again (within milliseconds of the previous call) and filling the interpreter stack with a recursive function that never ends.
Stack would look like:
animateScroll()
animateScroll()
animateScroll()
animateScroll()
animateScroll()
...
What you need is:
animateScroll()
animateScroll()
complete:function()
animateScroll()
complete:function()
animateScroll()
complete:function()
animateScroll()
...
so that each step completes before a new one is called.
I am implementing David DeSandro's JQuery Masonry plugin for a site i'm trying to build. I am trying to run a callback on the masonry function so that i can scroll to the relevant part in the page after it runs but for some reason can't get it to work when I have the animation turned on. The docs can be seen at http://desandro.com/demo/masonry/docs/#options. When I run the following code it works fine and the alert only happens once the masonry function has run:
$wall.masonry(
{
columnWidth: 216,
itemSelector: '.box:not(.invis)',
animate: false
},
function()
{
alert("Finished?");
}
);
However when i run the following code with the animation options included the alert runs before the animation has finished:
$wall.masonry(
{
columnWidth: 216,
itemSelector: '.box:not(.invis)',
animate: true,
animationOptions: {
duration: speed,
queue: false
}
},
function()
{
alert("Finished?");
}
);
I would really appreciate any pointers anyone can give me as to how to prevent the alert happening until the animation has completed as i'm stumped! Thanks so much for your help,
Dave
There's something you can do, but it to work in your sense requires a small hack:
The object to animationOptions passed can take a complete property, which defines a function which will be fired after the animation is complete.
The problem is, this will be the case for each and every of your blocks, since every block is animated separately. But you could get around this like so:
var boxCount = $wall.find('box').size(),
counter = 0,
onComplete = function() {
if (counter < boxCount) counter++;
else {
alert("Finished?"); // <-- Here goes your actual callback!
counter = 0;
}
}
$wall.masonry({
columnWidth: 216,
itemSelector: '.box:not(.invis)',
animate: true,
animationOptions: {
duration: speed,
queue: false,
complete: onComplete
}
});
Is it possible to run two animations on two different elements simultaneously? I need the opposite of this question Jquery queueing animations.
I need to do something like this...
$('#first').animate({ width: 200 }, 200);
$('#second').animate({ width: 600 }, 200);
but to run those two at the same time. The only thing I could think of would be using setTimeout once for each animation, but I don't think it is the best solution.
yes there is!
$(function () {
$("#first").animate({
width: '200px'
}, { duration: 200, queue: false });
$("#second").animate({
width: '600px'
}, { duration: 200, queue: false });
});
That would run simultaneously yes.
what if you wanted to run two animations on the same element simultaneously ?
$(function () {
$('#first').animate({ width: '200px' }, 200);
$('#first').animate({ marginTop: '50px' }, 200);
});
This ends up queuing the animations.
to get to run them simultaneously you would use only one line.
$(function () {
$('#first').animate({ width: '200px', marginTop:'50px' }, 200);
});
Is there any other way to run two different animation on the same element simultaneously ?
I believe I found the solution in the jQuery documentation:
Animates all paragraph to a left style
of 50 and opacity of 1 (opaque,
visible), completing the animation
within 500 milliseconds. It also will
do it outside the queue, meaning it
will automatically start without
waiting for its turn.
$( "p" ).animate({
left: "50px", opacity: 1
}, { duration: 500, queue: false });
simply add: queue: false.
If you run the above as they are, they will appear to run simultaenously.
Here's some test code:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function () {
$('#first').animate({ width: 200 }, 200);
$('#second').animate({ width: 600 }, 200);
});
</script>
<div id="first" style="border:1px solid black; height:50px; width:50px"></div>
<div id="second" style="border:1px solid black; height:50px; width:50px"></div>
While it's true that consecutive calls to animate will give the appearance they are running at the same time, the underlying truth is they're distinct animations running very close to parallel.
To insure the animations are indeed running at the same time use:
$(function() {
$('#first').animate({..., queue: 'my-animation'});
$('#second').animate({..., queue: 'my-animation'});
$('#first,#second').dequeue('my-animation');
});
Further animations can be added to the 'my-animation' queue and all can be initiated provided the last animation dequeue's them.
Cheers,
Anthony
See this brilliant blog post about animating values in objects.. you can then use the values to animate whatever you like, 100% simultaneously!
http://www.josscrowcroft.com/2011/code/jquery-animate-increment-decrement-numeric-text-elements-value/
I've used it like this to slide in/out:
slide : function(id, prop, from, to) {
if (from < to) {
// Sliding out
var fromvals = { add: from, subtract: 0 };
var tovals = { add: to, subtract: 0 };
} else {
// Sliding back in
var fromvals = { add: from, subtract: to };
var tovals = { add: from, subtract: from };
}
$(fromvals).animate(tovals, {
duration: 200,
easing: 'swing', // can be anything
step: function () { // called on every step
// Slide using the entire -ms-grid-columns setting
$(id).css(prop, (this.add - this.subtract) + 'px 1.5fr 0.3fr 8fr 3fr 5fr 0.5fr');
}
});
}
Posting my answer to help someone, the top rated answer didn't solve my qualm.
When I implemented the following [from the top answer], my vertical scroll animation just jittered back and forth:
$(function () {
$("#first").animate({
width: '200px'
}, { duration: 200, queue: false });
$("#second").animate({
width: '600px'
}, { duration: 200, queue: false });
});
I referred to: W3 Schools Set Interval and it solved my issue, namely the 'Syntax' section:
setInterval(function, milliseconds, param1, param2, ...)
Having my parameters of the form { duration: 200, queue: false } forced a duration of zero and it only looked at the parameters for guidance.
The long and short, here's my code, if you want to understand why it works, read the link or analyse the interval expected parameters:
var $scrollDiv = '#mytestdiv';
var $scrollSpeed = 1000;
var $interval = 800;
function configureRepeats() {
window.setInterval(function () {
autoScroll($scrollDiv, $scrollSpeed);
}, $interval, { queue: false });
};
Where 'autoScroll' is:
$($scrollDiv).animate({
scrollTop: $($scrollDiv).get(0).scrollHeight
}, { duration: $scrollSpeed });
//Scroll to top immediately
$($scrollDiv).animate({
scrollTop: 0
}, 0);
Happy coding!