A Modest Proposal for CSS3 Animations
I've been thinking quite a bit about CSS architecture these days.
One thing in particular that has crossed my mind is how to handle certain situations. For example, we want to hide content on the page and then reveal it (or vice versa). In JavaScript, this is relatively straightforward: get an element, and apply a class or remove a class to change the state of the element. The CSS for that might look something like this:
div { display:block; }
div.hidden { display:none; }
In this case, I've used display:none to hide the content visually and from screenreaders, too.
Adding a visual effect
Now, if I want to add a visual effect, I might use JavaScript to alter a style property from value A to value B (eg. style.opacity
) The script would run through the animation and apply the hidden class at the end of the animation.
What if we wanted to offset the visual effects over to CSS3 Animations?
@keyframes fade-out {
0% { opacity: 1; }
100% { opacity: 0; }
}
div { display:block; }
div.hidden { display:none; animation: fade-out .5s 1; }
Nice and easy! Or is it? For those that have actually tried this code might be surprised to discover that this doesn't work. It's because once the hidden class is applied to an element, it's immediately hidden with display: none;
.
The next thing you might think to do is apply display:block
in the keyframes like so:
@keyframes fade-out {
0% { display:block; opacity: 1; }
100% { display:none; opacity: 0; }
}
The problem with this is that non-transitionable properties like display are ignored and have no effect.
A Proposition
I propose that the CSS3 Animation specification be changed to allow for this. Keyframes should act like classes being applied to an element. Therefore, the example above is display:block at 0% and becomes display:none at 100%. As a result, the page works as expected for this scenario.
Transitioning properties
I woke up this morning thinking about this further—and I'll readily admit that this next idea complicates things a little bit. I additionally propose that an animation-transition-property property be added. This specifies which of the keyframe properties should actually transition.
@keyframes fade-out {
0% { opacity: 1; }
100% { opacity: 0; position: absolute; left: -999px; }
}
div.hidden {
animation: fade-out .5s 1;
animation-transition-property: opacity;
}
By setting the animation-transition-property, only the opacity will transition. At the end of the transition, the element is then placed offscreen using position and left properties. The default setting for animation-transition-property would be all
which would allow keyframes to behave exactly as they do today. There would also be a value of none
that would allow for no property to transition. This would allow for stepped animations to be created.
Reversing animations
My last proposal for CSS3 animations is the ability to reverse an animation. Right now, the spec has an alternate
property that allows every odd iteration to animate in reverse but that's not quite what we want. Again, allow me to demonstrate with an example:
@keyframes fade {
0% { opacity: 0; }
100% { opacity: 1; }
}
div {
animation: fade .5s 1;
}
div.hidden {
animation-direction: reverse;
}
In this example, I have a fade animation applied to the div. By default, it'll fade to 100%. When the hidden class is applied, the direction of the animation is changed and the fade goes from 100% to 0%. This allows animations to be quickly and easily re-used.
Standards Process
Writing a specification is hard and I don't envy those that have to work through these things. It's a balance between making something powerful and making something complicated. I hope that my ideas fall more in the former category than the latter.
Conversation
But... when you go in reverse does the display: none; or off screen positioning persist from 100% to 1%? Seems like the two somewhat conflict with each other in that sense.
It's almost like you want an "end" (101%) state that can be used to apply things like display:none; when and only when the animations is at the 100% boundary?
@Jordan: Admittedly, the example changes throughout the article. In order to do what you're asking, what you'd actually want is something like this:
Once it reaches 100%, the animation styles are removed—this is how it currently behaves, too.
I see. I've tried exactly the same things myself and I've had to resort to strange hacks to get a similar effect. I agree on both points. I would love to see this get adopted!
I've been playing with animations a bit lately, too, and agree that something needs to be done for some certain situations. But … I think a different, and maybe more simple approach is necessary.
Right now, this works:
Swapping the class on that DIV will fade it in and out smoothly. What won't work (and can be critical) is any style property that has a fixed number of values.
display
is certainly one of those.My solution relies more on defining properties as either "scalable" (color, font-size), or "fixed" (display, text-decoration) ... (these types of properties might already have names that I'm unaware of).
Scalable properties can be animated as normal. If a scalable property is not included in the transition definition (either explicitly or via an "all" or * nomenclature), then it is treated as fixed.
Fixed properties only apply at the end of a transition. If there are multiple transitions timings running on a single class, it applies at the end of all transitions. Additionally, a fixed property could be "animated" if you wanted it to occur at another time during the overall transition...
An example:
I'm using the -webkit-nomenclature since it works, but I think the idea is solid. It would allow you to animate non-scalable elements and set the time at which their effect occurs.
Be interested to hear your thoughts.
My properties weren't right above and that was bugging me. That'll teach me to try and write CSS without auto-complete!
Code still doesn't work, but at least the property names are right :)
I guess I'm not really clear why it would be preferable to have display:none / display:block able to be animated when the effect is actually possible using different style declarations to make it happen. Opacity works, albeit with the element continuing to take up space in the document flow (leaving behind a big space where it used to be) - but you could animate height or something along with it to make it fade out and appear to be removed from the document flow, right?
Is it just a matter of trying to have the effect take place using fewer style rules to make it so? Not complaining...just wondering.
@Bridget: in the example I used, display:none has advantages because it also removes the element from being read by screenreaders.
Can't that (display:none) be done after the animation is completed? Still, I understand that it would take more than one style rule to make all that happen. If it's just a matter of trying to make more things animatable for the sake of making it easier on authors, I have no real problem with that. hehe
This is a great feature suggestion, which is why it will be ignored by the CSSWG. :P
@Bridget: the only way to do it after the animation is complete is with JavaScript and you'd have to know exactly how long the animation would take in order to do it. With the approach I outline, you could actually do a pop-up dialog entirely with CSS. I believe these features will make it easier on authors while also making it more powerful.
@Jonathan: There is no need to know the animation duration, the "transitionend" event takes care of that. But anyway, being able to use CSS only would be great!
Whoups, in case of animations it would be "animationend".
I'd love to see this added in the spec.
You can technically animate only one property by setting the 99.9% keyframe rule? http://jsfiddle.net/nimbu/bBRzR/
I think the behavior you are proposing is definitely the expected and desired behavior. I'd wager that "do an animation then hide the element" is the most common problem trying to be solved with CSS animations, and should really be addressed in some way within the specification.
The only issue I see with your proposal is that all of the properties become encapsulated within the animation definition, and thus are lost on older browsers.
Perhaps a property that defines whether animations should occur before or after non-animated properties take effect would solve this problem in a more general way. Something like:
animation-order: before|after;
This way, if CSS animations aren't supported, all of the fixed elements still get applied.
I was afraid this post had something to do with eating babies.
Whenever I try to develop with CSS transitions, I find that invariably I want different effects for when the class is added than when it is removed. I also think that some of the problems this solution solves are solved now in other ways—object detection for graceful degradation, and animation events for setting non-transitionable properties. For example: http://jsfiddle.net/yabjH/1/
I feel that a property such as "
myClip._visible = false
" from ActionScript is necessary in CSS. Thedisplay
property deals with the formatting aspect of an element, not whether or not the element should be rendered by the agent. This property also does a disservice developers by requiring some programatic way (limited to JavaScript?) to remember what the previous value fordisplay
was used.A boolean visible-type property would also help out reduce memory and CPU usage when horrible hacks such as "
left: -999px;
" are used. Even though elements don't appear on the screen or outside a layer's bounds, they will continue to be rendered and processed by the agent.The more I think about it, the more I don't understand "
display: none
".An existing property that might help with some of the problems listed in the comments is -webkit-animation-fill-mode:forwards; It makes the styles in the last frame of the animation persist rather than reverting entirely to the class styles so this works to fade your element in from display:none:
@keyframes fade {
0% {opacity: 0; }
100% {opacity: 1; }
}
.thing{
display:none;
opacity:0;
}
.thing:target{
display:block;
-webkit-animation-name: fade;
-webkit-animation-duration: 1s;
-webkit-animation-fill-mode:forwards;
}
The thing that frustrates me about my above example is that it works as an animation but not as a transition applied specifically to the opacity property (-webkit-transition: opacity 1s linear;). Including one property that is not transitionable in either declaration causes the entire transition to fail, even though I don't want to transition it.
When I began experimenting with transitions I assumed when an element's stage changes all of the non-transitionable properties were changed instantly (essentially on frame 0) and then the transitions were applied (over the course of the specified duration). Not so. But this does appear to be the order of things if you are using an animation rather than a transition. WHY!??!?! I don't know and it makes my head hurt.
As for removing the document from the flow...we can't transition height or scale to remove the document from the flow either, we run into the same problems as display:none.
Additionally, height can't be transitioned from 0 to auto so if I need to element to fill it's natural space that's not an option.
Great tricks. Thanks for sharing.
ok nice job man.
Nice post, I really like this post style.
Ivan @ souplantation coupons 2011
Create Animated Alert Box using MooTools and a lightweight (~4.5kb) Custom JavaScript Dialog Boxes. Welcome, Visitor. Subscribe to our RSS Feed and coniedsr adding this article/site to your favorite social bookmark site if you find it useful. Thank you! | Subscribe to RSS