When running a Jquery animation like slideDown(), it looks like a number of element-specific css properties is set to be updated at a specific interval and when the animation is complete these properties are unset and the display property is simply set to auto or whatever. At least in firebug you can't see those temporary properties any more.
The problem I've encountered is the scenario where we stop the slide down with stop(). The element is then left with the current temporary css values. Which is fine because it has to, but let us say that I stoped the slidedown because I have decided to slide it back up again a bit prematurely. It would look something like this:
$(this).slideDown(2000)
//The below events is not in queue but will rather start execute almost
simultaneously as the above line. (dont remember the exact syntax)
$(this).delay(1000).stop().slideUp(2000)
The above code might not make much sense, but the point is:
After 1 second of sliding down the animation is stopped and it starts to slide back up. Works like a charm.
BUT!!! And here is the problem. Once it it has slid back up the elements css properties are reset to the exact values it had 1000ms into the slideDown() animation (when stop() was called). If we now try to run the following:
$(this).slideDown(2000)
It will slide down to the very point the prior slideDown was aborted and not further at half the speed (since it uses the same time for approximately half the height). This is because the css properties were saved as I see it. But it is not especially wished for. Of course I want it to slide all the way down this time.
Due to UI interaction that is hard to predict everything might soon break. The longer animations we use increases the risk of something like this happening.
Is this to be considered a bug, or am I doing something wrong? Or maybe it's just a feature that is not supported?
I guess I can use a callback function to reset the css properties, but depending on the animation used, different css properties are used to render it, and covering your back would result in quite a not-so-fancy solution.
You could try to replace the slideUp and slideDown with animate.
http://api.jquery.com/animate/
This way, you are explicitly telling it what to do.
This is the expected (though not desirable) behavior...to get the .slideDown() to go to the full height, start the slides from the finished position, by using .stop(true, true), so the animation completes. The second true argument, telling it to skip to the end of the animation is the important part here, so those "final" values it slides back to are the full height, etc...rather than the height it was at when it stopped sliding.
This was a bug that has been fixed in jQuery 1.7.2: http://bugs.jquery.com/ticket/8685
Related
i'm applying a css keyframe animation to an element. i only specify one keyframe (100%) for a simple transform. while the animation is running i pause using the animation playstate and apply a class specifying a different keyframe animation. what i want is that the second animation starts where the first animation was interrupted but instead the element jumps back to its start position and is animated from there. i played a bit with animation-fill-mode but it doesnt change which i think is because the animation was interrupted before it reached 100%. any ideas what i could do to make this work?
I was actually brainstorming this a couple of days ago. You are correct in assuming that your issue is the result of your animation not reaching 100%. The problem is that there is no way to simply select the values indicating how far your animation made it in the animation. From this, you have the following three options (note: I have not tested all of them):
You can break the animation into defined steps (10% intervals, 20% intervals, etc.) and then only pause it on one of the steps. This is probably the safest solution, but you will likely have to base it on time (i.e., setInterval, etc. - Yuck!)
You can calculate (also based on time - Double Yuck!) where the element is, using JavaScript, and set up a new keyframe accordingly
You can try to look at the element's properties at the time that the animation is paused (I have not tried this and I highly doubt it will work)
http://jsfiddle.net/gxve9/1/
Animations don't actually change the css, they just animate it and then put it back where it was. You just have to use javascript to save the css using something like
var prevWidth = $("div").css("width");
and then after pausing the animation, set it with
$("div").css("width",prevWidth);.
That way it stays permanently set where the first animation put it.
You have a few options here:
Append the new animation to the list with a different play state. This is not very scalable but works for some cases.
Capture the animated value using getComputedStyle and apply it yourself. The disadvantage of this is that for transform, the value returned by getComputedStyle is converted to a matrix() value so the interpolation for rotate animations will sometimes behave differently.
Use commitStyles() to do it for you. The trouble with this is browser support. It should work in the latest Firefox and Safari, and next version of Chrome (works for me in Canary).
You can see each approach demonstrated here: https://jsfiddle.net/birtles/8bv5of6n/14/
I'm currently making a little navigation menu. In one of those dropdown menus, I need one category to expand. It works. But, when category retracts with the help of jQuery .hide('slow'), the menu I want to hide blinks once at the end of the hide animation, and then is properly hidden.
Here's some code below to illustrate my problem :
$(document).ready(function(){
var verif = false;
$("a").hover(function() {
var texte = $(this).text();
var tab = new Array;
tab = texte.split(" ");
if (tab[0] === "+ Deroule" && verif === false) {
$("#submenu").show("slow");
verif = true;
}
else if (tab[0] === "+ Deroule" && verif === true) {
$("#submenu").hide(8000);
//There, I can clearly see, at the end of the hide animation, the previously opened content blinking once before it hides completely.
verif = false;
}
});
});
Without seeing your HTML/CSS it's hard to actually pin the problem down - hint hint create a fiddle hint hint :) - but the first thing that I would do, if I were you, is change the .hover() method to .mouseenter() and .mouseleave() (NOT .mousein() and .mouseout() - that's another conversation about IE support - you can/should look that up on jQuery's site, if you're not familiar).
Why do this? The .hover() method is actually just shorthand for .mouseenter()/.mouseleave() but, for various reasons (including the fact that it is not as widely supported as the two full methods), it occasionally fails to work properly. I cannot guarantee that this is the problem in this situation, but, especially since this appears to be a browser issue, it is always best to go back to basics and start with as much support as possible.
EDIT:
After seeing your simplified fiddle, I think I have a better understanding of what your issue is and what you're trying to do. I have had the same issue before and, from my experience, I can tell you that it is actually a difficult issue to account for.
Elements jump when they are shown/hidden using the .show(), .hide(), slide, and fade methods because as soon as the animation completes they are removed from the flow (in most browsers - not chrome - the show/hide methods do not cause the element to shrink all the way to nothing, so there is still choppiness). Here are two of my most recent solutions to this issue:
In one situation, I was tasked with creating view-more and view-less buttons inside a menu, which would make elements appear/disappear SMOOTHLY one at a time. If you simply try to call a show/hide, slide, or fade method on the collection to be shown, they will all animate at the same time, and the same thing happens when trying to make them run on callback. Additionally, sliding methods wouldn't work because this animation was going to be called on multiple items at a time, thus causing a lot of overhead and making the effect choppy in slower browsers (and even a bit in FF). The show/hide methods worked in some browsers, as they actually do reduce the size of the element, but they are choppy in others so they were out (effectively, their animations don't "complete" in all browsers - see above). Thus, I was left with three options: use fade, animate, or a combination of both.
Solution 1:
The original plan, as it was (with the given specs) the best solution at the time, was to use the animate method to simply slide a wrapper div (with overflow set to hidden up or down, based on the offset of a target element in the list). This worked great, but I was then given the requirement of having each element disappear in succession, via a fade effect. If you do choose this method, then I suggest that you instead use slide methods - as you are not attempting to show view more/less links, this shouldn't be a problem (yes, I know that this probably doesn't sound like it would be an issue for view more/less links, but the way I had to implement it, slides actually did cause an issue).
Solution 2:
Using fade on its own was an issue because everything was still going to fade at the same time and, even if they didn't, the animation would still jump once the fade finished. Thus, the first issue I needed to solve was the animations running at the same time. Using a timer was out of the question because of the overhead, so I wrote a function that recursively called the fade on each element in the collection after calculating the necessary delay between calls (based on a supplied speed argument, and the number of elements to be faded). Next, I still had to solve the jumping issue once the animations were complete. To fix this, I implemented the .fadeTo() method, thus keeping the elements in the flow of the document after their animations. This did, however, cause two more issues:
The .fadeTo() method is not supported in IE (it may be in 9 and 10 - I can't remember)
After the animation, there was a lot of empty space due to the elements not being removed from the flow
To remedy the first issue, I added a browser-specific method for IE, which set the display property of each element to block and their visibility to hidden once their fade-outs were completed - it also did the reverse just before their fade-ins were started.
I solved the second issue by using the animation method that I described previously. To make this work, I had to write another auxiliary method that calculated the speed at which the container needed to slide, based on a given speed of the animation and the number of elements being shown/hidden.
I know that this was a lot of detail, but I hope it helps you determine how best to go about this.
Let me know if you have any questions or need any clarification when you move forward with your implementation. Good luck! :)
I am currently trying to have an image move around the screen in certain paths. When it finishes one, it hides itself, then moves to the start point again to wait for user input and start the next. I'm using the anim function in Zepto for both of these, however I am noticing that when animated while hidden, the program crashes. Can someone tell me how I can fix this, either through a different way of moving it or something I need to do with anim()?
Thanks for the help.
What do you mean by crashes? I am guessing you are using a callback when your animation completes to trigger the next step AND you are using either display:none or visibility:hidden to hide your element.
If this is the case, the problem you are facing is that the anim callback does not fire when no animation takes place. The callback is based on the webkitTransitionEnd function which only fires if a transition occurs. These transitions won't actually occur for A) boolean properties like visibility and B) objects that are completely hidden and not being rendered.
The easiest way to overcome this would be to have your image never be removed from rendering, by disappearing it using opacity: 0 or changing its z-index to be below all other elements. Typically, what I do is have two states: {opacity:1, zIndex: 10000} and {opacity:0, zIndex: -1}. This way, when the object is completely faded out it won't block other elements and it will fade smoothly. (zIndex from -1 to 1 is happens at very low opacity.)
I'm using jQuery jCarousel for a slider, after few mins I found the way to stop the slider on mouseover, but the problem is it doesn't stop immediately on mouseover, it only stop at the end of the image set (3 images in my demo). However it does start immediately on mouse out. I'm new javascript stuff and couldn't do much by diving to it's source. :(
My Current Code - http://bit.ly/ijNcow
Thank You
Often, we manage a jCarousel by using .click(). In this case - you can use the following code to stop the animation:
itemLoadCallback: {
onBeforeAnimation: function(jc, state) {
jc.lock();
}
}
The jc.lock() or jc.unlock() functions stop and resume the animation, respectively.
The jCarousel code uses two timers that I can see. The timer wrapped in jQuery's animation support and a timer that scrolls the underlying list forward by one item. Your page's code clears the second timer by calling carousel.stopAuto(), but not the animation timer. Hence the animation continues until the next item has been scrolled in (in essence, that the second item reaches the topmost position).
To clear that one you'll have to also call jQuery's stop() function on the carousel element. At a guess (no, I have not tested this), you should call
$('#listing').stop(true);
Having said that, I'm not sure what will happen to the user experience if you've paused half way through an animation and then started it up again (by moving the mouse out, say). The animation won't start up where you left off, that's for sure. I would also guess that the carousel will assume that the animation completed normally and you'll get a "jump effect" as it sets the top item properly in place.
I have the following working code to make a element of my scrollbox visible:
var next = elements.item(i+1);
var xpcomInterface = scroll.boxObject.QueryInterface(
Components.interfaces.nsIScrollBoxObject);
xpcomInterface.ensureElementIsVisible(elements);
But I would like to make a smooth-scroll (slow or not). Any idea how to do it?
update
By the way, it's for Mozilla environment.
The simplest way would be to just use a setTimeout function and just keep moving the top and left value on the element's div by a small number, until you get where you want to be.
You may want to experiment with how fast to make it move, as there is a trade-off with smooth, and the fact that it should reach the endpoint in some reasonable time.
Update:
I forgot, you will want to keep calling the setTimeout until you reach the final destination, otherwise it won't redraw the browser window.
I don't know exactly how to do it, but here's a Chrome Extension called "Smooth Scroll" where you can potentially pick through the code to see what they do and maybe get you in the right direction.
PS I love this extension.
jQuery .animate() with accelerated motion can be kind of smooth (see demo for animate with a relative move). And with a plug-in, there are other easing equations it can use.