There are several tricks to writing efficient CSS animations that few people seem aware of. Most people borrow directly from common libraries such as Animate.css without realising how bloated they are.
You can:
- Omit start or end frames for greater versatility with less code.
- Combine similar frames.
- Combine animations to reduce keyframes declarations.
- Reverse animations to avoid separate "In" and "Out" keyframes.
- Use negative delays to run only portions of animations.
Most animation libraries including Animate.css define both start and end frames. This is unnecessary:
If a ‘0%’ or ‘from’ keyframe is not specified, then the user agent constructs a ‘0%’ keyframe using the computed values of the properties being animated. If a ‘100%’ or ‘to’ keyframe is not specified, then the user agent constructs a ‘100%’ keyframe using the computed values of the properties being animated.
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
}
@keyframes fadeOut {
to {
opacity: 0;
}
}
Only define a start frame if you are animating in or an end frame if animating out. Note: It's best to only define "In" animations and reverse them when needed, see Reverse animations below.
Vendor prefixing significantly bulks out whatever you do with @keyframes
so dropping a frame can mean much less CSS. More importantly, your animations will be more versatile.
An example is the easiest way to explain why. Say you have a semi-opaque image you want to animate onto the page with a fade-in effect:
img {
opacity: 0.5;
animation: fadeIn 0.4s;
}
Typical fadeIn
keyframes animate the opacity from 0
to 1
over .4s
, resulting in a blink when the the animation is over and the opacity reverts to the .5
defined.
Our better fadeIn
animates the opacity from 0
intelligently to the .5
defined. No blink!
This approach can eliminate the dreaded blink for any valid animation.
@keyframes flash {
25% {
opacity: 0;
}
50% {
opacity: 1;
}
75% {
opacity: 0;
}
}
@keyframes flash {
25%,
75% {
opacity: 0;
}
50% {
opacity: 1;
}
}
Instead of maintaining a long list of separate animation keyframes as found in most libraries:
animation-name: fadeIn
animation-name: slideInRight
animation-name: fadeInRight
Combine compatible animations instead:
animation-name: fadeIn
animation-name: slideInRight
animation-name: fadeIn, slideInRight
In this example we have achieved the same three animations with only two keyframes.
Note that you can only combine compatible animations. Each must be working different properties, in our example fadeIn
works opacity
while slideInRight
works transform
.
If you're willing to reverse animations you can avoid defining separate "In" and "Out" animation keyframes:
@keyframes fadeIn {
from {
opacity: 0;
}
}
.fade-in {
animation: fadeIn 0.4s;
}
.fade-out {
animation: fadeIn reverse 0.4s;
}
When you want to run only a portion of an existing animation use a negative animation-delay
to avoid a separate keyframes declaration.
If the value for ‘animation-delay’ is a negative time offset then the animation will execute the moment it is applied, but will appear to have begun execution at the specified offset. That is, the animation will appear to begin part-way through its play cycle.
Imagine you have a zoom-in animation that looks great for small elements and a similar, less exaggerated effect for larger elements…
@keyframes zoomIn {
from {
transform: scale(0);
}
}
@keyframes zoomInHalf {
from {
transform: scale(0.5);
}
}
.zoom-in {
animation: zoomIn 1s;
}
.zoom-in-half {
animation: zoomInHalf 1s linear;
}
@keyframes zoomIn {
from {
transform: scale(0);
}
}
.zoom-in {
animation: zoomIn 1s;
}
.zoom-in-half {
animation: zoomIn 2s linear -1s;
}
Note:
- The duration has been doubled to account for it being cut in half by the negative delay.
animation-timing-function
influences what effect negative delays will have.linear
is easiest to understand: Half the duration results in half the effect. The first half ofease
has the the most action, so cutting it off results in a pretty boring animation. Play around with the duration and how much you cut off to get the effect you're after.