I was working on making my animations work dynamically for various elements when I ran into this problem (right when I was about to finish too). I can't animate properties using the JQuery css() method. I was able to get the animation working with hard coded values for the height, width, and top properties. Here's the snippet:
function shrinkSection(section){
var elem = section.SectionID;
$(elem).find(".content").fadeOut(500);
$(elem)
.animate({top: $(elem).css('top'), height: $(elem).css('height')}, 500)
.animate({width: $(elem).css('width')}, {duration: 500,
complete: function() {
$(elem).find(".icon").fadeIn(500);
$(elem).addClass("active");
}
});
}
console.log() reveals that $(elem).css('[PROPERTY]') IS returning the correct css value. Any ideas why this won't work?
The animate() method queues the effects and waits till the first effect animation is finished to start the second one, so they are serially applied. From jquery docs:
queue (default: true)
Type: Boolean or String
A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string. When a custom queue name is used the animation does not automatically start; you must call .dequeue("queuename") to start it.
If you want the animations to happen in "parallel", you can do:
$(elem).find(".content").fadeOut(500);
if( $(elem).length )
$(elem)
.animate( { top: $(elem).css('top'), height: $(elem).css('height')},
{ duration: 500,
queue: false,
complete: function() { alert('callback!!'); } }
)
.animate( { width: $(elem).css('width')},
{ duration: 500,
queue: false,
complete: function() {
$(elem).find(".icon").fadeIn(500);
$(elem).addClass("active");
}
});
else
alert("No elem found!!");
Notice in the example, that you could check that the element elem on which you apply the effect is actually found by jquery.
This way you track possible DOM selector problems also.
Related
I am trying to loop through an animation selecting multiple elements and moving them as long as the mouse hovers over the parent area. This works well enough, but each time the animation loops through the first element (child) moves faster than the others. ??? JSFiddle Example
HTML:
<div id="menuContent">
<button id="btn1" class="mainButton" left="0"/>
<button id="btn2" class="mainButton" left="0"/>
<button id="btn3" class="mainButton" left="0"/>
</div>
jQuery:
$("#menuContent").hover(function () {
loop();
}
, function () {
stop();
}
);
function stop() {
$(".mainButton").stop();
}
function loop() {
$(".mainButton").stop().animate({ left: "+=20"}, 100, 'linear', function () { loop(); });
}
From the documentation:
complete
A function to call once the animation is complete, called once per matched element.
When you call animate it starts 3 animations. Animation for the first element gets started and finished first. And then its complete gets called and you stop and start all animations, though some of them didn't get completed yet.
Consider this example (Fiddle):
function loop() {
$('.mainButton').stop().animate({
left: '+=1'
}, 1, 'linear', function() {
loop();
});
}
Only one circle will be moving because there is no time gap for others to move.
You can use promises to make it work (Fiddle):
$('#menuContent').hover(function() {
$('.mainButton').data('run', true);
loop();
}, function() {
$('.mainButton').data('run', false);
});
function loop() {
if (!$('.mainButton').data('run')) return;
$('.mainButton').animate({left: '+=10'}, 100, 'linear').promise().done(loop);
}
Danil Speransky is correct. However there is an options argument to the animate function to allow animations to not run in a rigid queue.
`$(".mainButton").animate({ left: "+=20"},{queue: false}, 100, 'linear', function () { loop();});`
Check out the documentation for queue:false here.
You're mileage may vary, but doing these two things seem to help a lot:
First, store the jQuery object for the .mainButton elements:
var $mainButton = $('.mainButton')
Second, Make the left increment more and also increase the delay:
$mainButton.stop().animate(
{ left: "+=1000"},
5000,
'linear',
function() { loop() })
You can toy with the numbers more to see if you get even better performance.
https://jsfiddle.net/wotfLyuo/8/
Your complete handler is called, if the animation for one elements in the jquery collection is finished. So when the first element is finished, you call loop and stop the animation of the other elements. Better use promise and done and save the state of your animation within the collection:
$("#menuContent").hover(function () {
start();
}, function () {
stop();
});
function start() {
$(".mainButton").data('stopped', false);
loop();
}
function stop() {
$(".mainButton").data('stopped', true).stop();
}
function loop() {
var $mainButtons = $(".mainButton").stop();
if(!$mainButtons.data('stopped'))
$mainButtons.animate({ left: "+=20"}, 100, 'linear').promise().done(loop);
}
Here is a working fiddle (https://jsfiddle.net/wotfLyuo/5/)
I'm foolin around with the jquery hover functionality. the current code snippet looks like this:
$leftColumn.children().first().hover(
function(event) {
var $this = jQuery(this);
$this.css({
'background-color': '#505050'
}).parent().stop()
.animate(
{
'z-index': '999',
width: '220px'
},
{
duration: '1000'
}
);
},
function(event) {
var $this = jQuery(this);
$this.parent().stop()
.animate(
{
width: '38px',
'z-index': '1'
},
{
duration: '1500',
complete: function() {
$this.css({
'background-color': 'transparent'
});
}
}
);
}
);
What this basically does is increasing the width of a div (which is position absolute) to overlay another div.
I choosed to use jQuerys animate() functionality instead of CSS3s transition because I want to trigger a callback whenever the closing (decreasing the width again) animation is done.
My problem now is, that I want to delay the closing animation for 2 seconds (and yes I know about the delay() vs setTimeout() discussion) which worked fine with setTimeout(). However as the animation is timed out for the given duration it will run, even if I enter the hoverable area again. This of course makes sense as the stop() only triggeres while an animation is on the go, which is not the case if it is timed out.
How can I make this thing work (stop the closing animation when reentering the hoverable area) and still keep a timeout / delay before decreasing the width on "hover leave"?
jQuery Animate does unintended things to my CSS. When animating paddingLeft, it sets div to display: none; that's unintended and I can't figure out why it does that. JSFiddle
<script>
$("#menu-toggle").click(function(event){
event.preventDefault();
if($('#sidebar-wrapper').width() == 0){
$("#sidebar-wrapper").animate({
width: 'toggle'
}, {
duration: 600,
queue: false,
complete: function() { /* Animation complete */ }
});$("#page-content-wrapper").animate({
paddingLeft: 'toggle'
}, {
duration: 600,
queue: false,
complete: function() { /* Animation complete */ }
});
}else{
$("#sidebar-wrapper").animate({
width: 'toggle'
}, {
duration: 600,
queue: false,
complete: function() { /* Animation complete */ }
});$("#page-content-wrapper").animate({
paddingLeft: 'toggle'
}, {
duration: 600,
queue: false,
complete: function() { /* Animation complete */ }
});
}
});
</script>
SOLVED:
Here's a Solve if anyone is interested: FixedFiddle
From the Docs:
In addition to numeric values, each property can take the strings 'show', 'hide', and 'toggle'. These shortcuts allow for custom hiding and showing animations that take into account the display type of the element. In order to use jQuery's built-in toggle state tracking, the 'toggle' keyword must be consistently given as the value of the property being animated.
http://api.jquery.com/animate/
If my memory serves, and as the docs indicate, these shortcuts do extra work to literally "show" and "hide" the element on animation start and complete. It would seem jquery takes into account the initial display and then stores that to restore it later. Toggle is just a macro of "show" and "hide" and seems to function in the same way.
Hi I'm trying to do a simple chain animation in jQuery, with a pause (setTimeout) between each frame.
Say each div animates in with a duration of 3500. I would like to control the duration between each opacity fade in animation. Say between the first div and 2nd div the duration is 5 secs, and maybe 10 secs between the 2nd and 3rd frame.
How would you go about this?
http://codepen.io/leongaban/pen/Feroh
Current code
$('#blue').animate({
opacity: '1'
}, 3500, function(){
// Need 5 sec pause here
$('#blue').fadeOut('fast');
$('#orange').animate({
opacity: '1'
}, 3500, function(){
// Need a 10 sec pause here
$('#orange').fadeOut('fast');
$('#green').animate({
opacity: '1' }, 3500);
});
});
That's what delay() and queue() is for:
$('#blue').animate({opacity: '1'}, 3500).delay(5000).queue(function() {
$(this).fadeOut('fast');
$('#orange').animate({opacity: '1'}, 3500).delay(10000).queue(function() {
$(this).fadeOut('fast');
$('#green').animate({opacity: '1'}, 3500);
});
});
FIDDLE
This is exactly what .delay() is for (http://api.jquery.com/delay/). It allows you to write elegant chains of animations for individual elements like this:
$( "#foo" ).slideUp( 300 ).delay( 800 ).fadeIn( 400 );
Note that you will still need to use callbacks to start animations for other objects, though.
In your case, this should be it (untested):
$('#blue')
.animate({ opacity: '1' }, 3500)
.delay(5000)
.fadeOut('fast',
function() {
$('#orange')
.animate({ opacity: '1' }, 3500)
.delay()
.fadeOut('fast',
function() {
$('#green')
.animate({ opacity: '1' }, 3500);
});
});
You can use jQuery fadeOut/fadeIn methods with callbacks.
See here for more information.
But essentially is;
$(".myClass").fadeOut(1000, function() {
//fadeOut complete
});
The first argument is length of time (in ms) until it completely fades out. After that duration has passed the callback fires. So you can safely assume that when the callback fires that your required waiting time has completed.
It's the same syntax for fadeIn also, but I suggest reading the link I provided. It'll explain it it greater detail.
Hi I'm having a div which width is increased with jquery. I want when that width reaches 100% to do something
$(function() {
$('.play').click(function() {
$('.loader').animate({
width: "100%"
},1500);
$('.video img').attr('src','css/images/movie-click.jpg')
$(this).hide();
if($('.loader').width()==$('.video img').width()) {
$('.video img').attr('src','css/images/movie.jpg')
}
});
Something is not right in the if statement. if someone can help me. My idea is to check if the with is 100%, and if it is, everything to be back to normal.(play to be showen, width=0%, img attr different.)
Use the complete callback in animate to execute code once the animation is finished:
$('.play').click(function () {
$('.loader').animate({
width: "100%"
}, 1500, function() {
// any code here will run only after the animation is complete
$('.video img').attr('src', 'css/images/movie.jpg');
});
// any code here will run as soon as the animation starts,
// before it's completed
});
The code in the click function is only called once, so your if statement is not being ran continuously as it animates. Instead, simply add that logic to a callback after the .animate() is complete:
$('.loader').animate({
width: "100%"
},1500,swing, function() {
$('.video img').attr('src','css/images/movie.jpg');
});
Something is not right in the if statement
Indeed :-) Your current code just compares the css values returned by the two .width() calls - which will be false of course.
That's not how you wait for an animation. The animation code is asynchronous, your animate() call just starts the animation but returns immediately (and goes on hiding the button and evaluating the condition).
Instead, pass a callback function to animate, it will be executed when the animation has completed:
$(function() {
$('.play').click(function() {
$(this).hide();
var $img = $('.video img');
$img.attr('src','css/images/movie-click.jpg');
$('.loader').animate({
width: "100%"
}, 1500, function() {
// executed after the animation
$img.attr('src','css/images/movie.jpg');
});
});
});