This content originally appeared on Bram.us and was authored by Bramus!
With @property
support being available in Chrome for a long time and now in Safari Technology preview too, it’s time to warn about its big gotcha: when animating registered custom properties, they prevent hardware acceleration for properties that rely on them.
~
# @property
101
@property
is an at-rule that allows you to register your CSS Custom Properties. You give them a certain type (syntax), an initial value, and can control whether they should inherit or not. By registering a custom property to be of a certain type, the browser knows how to interpolate its values when used in transitions and animations.
@property --angle {
syntax: '';
initial-value: 0deg;
inherits: false;
}
@keyframes adjust-angle {
to {
--angle: 360deg;
}
}
div {
--angle: 0deg;
animation: 10s adjust-angle linear infinite;
rotate: var(--angle);
}
More details and examples can be found on web.dev and in Exploring @property
and its Animating Powers
~
# One CSS Custom Property to rule them all
⚠️ This demo relies on CSS features that are not supported by all browsers yet. For the time being, please use Chrome 111+ (= current Canary) or Safari Technology Preview 162+.
Lets build a demo which animates two aspects of a box at the same time:
- Rotate the box from
0deg
to360deg
- Move the box down and up the y-axis over a distance of
100%
on each side
Thanks to @property
, combined with Individual Transform Properties and Trigonometric Functions, this becomes easy to do. Instead of animating the rotate
and translate
properties separately, you can animate a --angle
custom property from 0deg
to 360deg
, and use its value in the rotate
and translate
properties.
@property --angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
@keyframes animate {
from {
--angle: 0deg;
}
to {
--angle: 360deg;
}
}
.box {
animation: animate 5s linear infinite;
transform-origin: 50% 50%;
rotate: var(--angle);
translate: 0 calc(sin(var(--angle)) * 100%);
}
As --angle
constantly gets updated, so will the rotate
and translate
properties that depend on it.
See the Pen Animation in CSS, using a Custom Property by Bramus (@bramus) on CodePen.
For comparison, here is an alternative version that does not use a custom property
@keyframes animate {
from {
rotate: 0deg;
translate: 0 0;
}
25% {
translate: 0 100%;
}
50% {
translate: 0 0;
}
75% {
translate: 0 100%;
}
to {
rotate: 360deg;
translate: 0 0;
}
}
.box {
animation: animate 5s linear infinite;
transform-origin: 50% 50%;
}
Visually, this code has the same outcome:
See the Pen Animation in CSS, not using a Custom Property by Bramus (@bramus) on CodePen.
Personally I find the first approach – the one using the --angle
custom property – easier to grasp, build, and maintain.
This “One CSS Custom Property to rule them all”-approach is a common technique: by simply flipping a few switches you can have your layout respond to it. Take this demo by my colleague Jhey for example: only the --hue
value changes, and all stripes of the rainbow respond to that change. Easy.
See the Pen Animated Custom Property by Jhey (@jh3y) on CodePen.
~
# The gotcha
Even though both box-demos both have the same visual outcome, the version that relies on --angle
has a problem, as surfaced through a performance inspection:
Even though code>rotate and translate
are typically properties that are animated on the compositor thread with the help of the GPU, this is not the case here: layout is constantly being trashed and it gets rasterized on every frame!?
Zooming in on the timeline, we see style constantly being invalidated, a successive style recalculation being triggered, and eventually a repaint being done.
Compare this to a trace of the demo that does not use the Custom Property to drive the animation.
As the timeline shows, this version is buttery smooth and does not need to constantly recalculate styles – it runs on the compositor, as one would have expected.
~
# But why?
The culprit is the registered --angle
custom property that’s being used to control the other properties. Digging into the specification, it becomes clear what goes on:
[T]he value of a registered custom property can be substituted into another value with the
var()
function. However, registered custom properties substitute as their computed value, rather than the original token sequence used to produce that value.
So as the animation runs, the value of the --angle
custom property gets updated on every frame. Because it gets passed as a computed value, style gets invalidated and a new value is computed. Once that’s done, the properties that rely on it require a repaint. Rinse and repeat.
What is interesting here, is what happens when you disable the rotate
and translate
properties using DevTools. When doing so, repaint stops from triggering but style invalidation still happens by simply having the animation run, even though --angle
is not used anywhere.
~
# Can this be fixed?
If the compositor were to be able to do var-substitutions, this could be fixed. In the box example, the compositor would need to figure out a way to prevent the --angle
custom property from causing a style invalidation while being animated, thereby preventing everything that follows.
Asking Chromium engineer Rune Lillesveen (futhark), he mentioned that it would require a somewhat deep understanding of such
– It doesn’t seem to be impossible, but definitely would require a substantial amount of work.var()
substitutions on the compositor
At the time of writing, this optimisation might seem unnecessary, but I guess that’s because usage of @property
today is low. My guess is that the need will become more urgent, once Safari and Firefox ship @property
as well, and people start actively relying on this.
Either way, go star Chromium issue #1411864 to signal interest if you want to see this improvement happen.
~
🔥 Like what you see? Want to stay in the loop? Here's how:
This content originally appeared on Bram.us and was authored by Bramus!
Bramus! | Sciencx (2023-01-31T23:12:10+00:00) The gotcha with @property. Retrieved from https://www.scien.cx/2023/01/31/the-gotcha-with-property/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.