I have a div and I want it to make a transition from font-size 20px to font-size 40px and also change its color. I know I can make this with maaany other alternativas, but I want to explore the usage of "transition" and I think something is wrong.
If I do this:
$("#xxx").css({transition:"all 1500ms ease-out 0ms",color:"#00FFFF", fontSize:"40px"});
The jQuery should first set the "transition", and after that set the color and font-size AND THE BROWSER should make the transition. But it's not the case, the font-size and color are applied immediatly.
What is even strange is that if I first set the transition and after 2 seconds set the font-size and color, then the transition will happen smoothly. Why?
Check the example below. If you open the link the browser will apply the color and font-size immediatly, instead of making the transition smootly. Why?
https://jsfiddle.net/hw33bghm/
The reason why this isn't behaving in the way that you expect is due to how reflow works in the DOM. Reflow is the web browser process for re-calculating the positions and geometries of elements in the DOM, and can be triggered by many things, including in this case, adjusting the CSS properties of an element.
When you make the $("#xxx").css({...}); call with multiple CSS properties you are giving the browser a batch of layout operations to perform and it will perform all of them in a user-blocking manner. Your styles are being applied immediately because there is no transition property until the operation is done and your new DOM is rendered.
Further, all modern browsers have optimizations to minimize reflow (again, it's a blocking operation and as such can affect performance), so if you simply separated setting your properties:
$("#xxx").css({transition:"all 1500ms ease-out 0ms"});
$("#xxx").css({color:"#00FFFF", fontSize:"40px"});
You might expect it to set the transition property, reflow, then set the color and fontSize, triggering the transition and working the way you intended. It won't though, those operations will be batched by the browser and again everything will happen in a single reflow.
This is also why it does work when you wait a second before setting the color and fontSize. Those layout operations are no longer batched together and so this time you have a transition property when the CSS for the other two are set.
For a little more information check out: https://developers.google.com/speed/articles/reflow#guidelines
Related
The short: I'm having trouble understanding Vue transition groups, the offical docs, recommended practice, and how to combine all that to get what I want.
What I want is a list of items that when new ones appear they transition from green to white and slide into the list. Then when they leave, they transition from red to white and slide out of the list.
If you want what I have so far, here's a js fiddle I made that was derived from the vue doc's list transition example. It's almost there, the only thing that isn't working is that the leaving items don't transition from red to grey.
Everything below is what I've researched to get to this point.
The official docs says I can apply transitions in .enter-active and .leave-active since they are applied throughout the entire animation.
v-enter-active: Active state for enter. Applied during the entire
entering phase. Added before element is inserted, removed when
transition/animation finishes. This class can be used to define the
duration, delay and easing curve for the entering transition.
v-leave-active: Active state for leave. Applied during the entire
leaving phase. Added immediately when leave transition is triggered,
removed when the transition/animation finishes. This class can be used
to define the duration, delay and easing curve for the leaving
transition.
And later, the List Transition docs applies the transition css statement to the element's own class instead of any of the transition classes automatically applied by vue. This makes some sense, as even the items that aren't actively leaving or entering still need to transition around those that get added or removed.
But then I stumbled upon this github post where a VueJs member described an issue with applying transitions in .active classes:
It's true that this behavior can be a bit confusing but it's actually working correctly because of the transition being applied but not being left enough time to change the color:
The .leave and .leave-active classes are applied at the same time. Because element already has a background-color, this triggers a 5s transition from grey to red, but one frame after the leave class is removed and leave-to is adding, triggering a new transition to green. So the color never gets enough time to change to red.
The solution is not applying the transition on leave-active but only on leave-to so the first change do not apply a transition: http://jsfiddle.net/posva/o4Lqw5ts/):
The problem do not appear on enter because the browser do not get the time to render the element without enter so it appears with the blue initial color and triggers the transition from there
Introductory information:
I've made a fixed menu button to show the navigation menu when using a mobile device. For this application I'm using the Headroom.js script to make the button smaller when scrolling downwards to ensure that it doesn't block too much of the content. The animation/transition is applied by adding a class with the given changes.
In the original method i changed the size and look of the button by changing height/width of the parent element and padding of the child element with CSS (and css transition).
The new method, which I've read could/should be better according to various sites, is changing the size of the button by using transform: scale(). Note that i'm also moving the element a bit by also applying translate3d(20px,20px,0) in this method. However, it feels a bit smoother when scrolling using the transform: scale() method (could be a placebo effect though), but using chrome dev tools' timeline gives me seemingly inconclusive results.
Therefore a part of my question is also how I should evaluate the best method. Is timeline in Chrome Dev tools the best option, or is there a better way to do it? And which elements of the timeline should I base my choices on? and the other thing is, based on your interpretation of the images and/or tests combined with your knowledge, which method performs the best (or should perform the best in theory)?
Beneath you can see two examples of the timeline with each method.
Changing height/width and padding (original method):
Method using transform: scale() to change the size:
Also you can try the different methods in fiddles here:
link: Original method changing height/width and padding
link to new method: using transform:scale
Please ignore the poor layout of everything; especially the button. The ugly image inside the menu button is just to show, that there's an image included in the layout on my own page and to take that into performance considerations. The images in the back is also included since it's a webshop with a lot of images which could influence performance.
CSS for added class that makes the changes in the original method:
.mobile-nav.headroom--unpinned {
height: 40px;
width: 40px;
}
.headroom--unpinned .mobile-content{
padding-top:4px;
}
CSS for the added class using transform:scale():
.mobile-nav.headroom--unpinned {
transform:scale(.5) translate3d(20px,20px,0);
}
So to summarize my questions:
How do I evaluate which methods has the best performance, and which method would you say performs the best?
A final note: I know that the methods are different (animating different things and more elements in the original method) but these are the 2 options which i prefer as it is right now.
I believe you are missing the point, Chris: the reason why no other property but transform and opacity should ever be animated is because they don't trigger a repaint in anything else, even if the element is in the document flow (and because you can basically do anything with these two alone in like 95% of the cases).
From the "hit-on-performance" point of view, there are two types of animations:
those that trigger a repaint in other elements than the animated element
those that do not.
That's the main reason behind recommending animations by transform, opacity or position:relative;left|right|top|left. Because they don't actually move the element in flow, thus not triggering a repaint to every single other element in flow after the one being animated.
Now, if the said parent was positioned absolute (which I assume to be the case), it wouldn't have triggered a repaint to the rest of DOM anyway so the differences between that method and transform would have been minor. Inconclusive, as you put it. In theory, repainting two elements instead of one should be slower.
If you need to test, make 10k clones and trigger animation on all of them, with each method.
That will be conclusive.
If you really want to min-max this (as in spend absurd amounts of time on hardly noticeable improvements, as I do) you will find plenty of resources that will recommend:
replacing any .animate() with .velocity()
never animating anything but transform or opacity, although Velocity claims they animate anything without a hit on performance (i find that debatable/arguable, at best) - but it's a net improvement over .animate()
sticking to CSS transitions, if possible (basically if you don't need chains)
using Web Animations API
Personal advice: never count on synced CSS animations, especially when you have many of them. If you change tabs or the system does something extremely resource heavy for a while, your animations will be way off. If you need chains, chain.
I understand and use both transitions and animations, but it seems to me that there is a bit of a "hitch" as it were when trying to combine them. For example if you have an animation style that ends on a color, and sets the value indefinitely with -webkit-animation-fill-mode: forwards; it would make a lot of sense if I could smoothly transition from this state to another one if an overriding style could be set.
See jsfiddle. Note that toggling the yellow style uses the transition smoothly, but the use of animation can only smooth the initial 0-50% during the animation. There is no way to recover the final green state to either plain white or yellow background without an abrupt instant change that occurs when the animation value is unset. Note also that the animation, when active, completely overrides the !important style.
I don't know how much of this behavior is specified in the spec/WD/etc., but as far as I'm concerned this type of limiting behavior makes CSS animations next to worthless, and the animation would be advisable to perform step-wise from javascript, used with transitions. The downside to that is the greatly added overhead of doing it that way.
And to address the "unclear what I'm asking" vote, what I'm asking is how can I make an animation that can interpolate back out of the final animation state?
True, but have you tried use the value reverse in the animation-direction property for your animation?
I have a script that applies certain css styles to an element, then adds a transition style to the element, and then applies another css style to that element. What I'm trying to do is have the element get styles applied to it instantly, and then animate the next change. The code is basic, just set styles, then set transition styles, then set the final styles. But I'm experiencing that the first property being changed (the one without the transition) is having a transition applied to it, even though I do not set the transition property until afterwards. I have double checked that the element does not already have a transition property applied to it. Why is this?
Also, If I leave a 50 millisecond delay between applying the first styles and the transition, it works as expected.
You have to force a relayout after the first styles are applied (so they are processed without transitions) and then you can apply the styles that lead to a transition. The way you are doing it now, all the opeartions are being collapsed into one operation and thus everything is undergoing the transitions.
The simplest way to get the relayout is to apply the first CSS properties, then do a setTimeout(fn, 1) to apply the second set of properties in the timer callback. There are also other ways to force a relayout by requesting certain properties that trigger a relayout. I don't remember exactly which properties those are off the top of my head (would take some research).
I haven't tried this myself, but I think requesting a size property on your element such as .offsetHeight will force the relayout. The browser realizes that there are pending style changes and that those pending style changes might affect the size request so it does a relayout synchronously before returning the .offsetHeight value, thus solving your issue.
A somewhat similar question and answer: "Force Reflow" in CSS transitions in Bootstrap
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/