I not too long ago up to date my portfolio at johnrhea.com. (In case you’re wanting so as to add a CSS or front-end engineer with storytelling and animation expertise to your workforce, I’m your man.) I preferred the look of a sequence of planets I’d created for one more private challenge and determined to reuse them on my new web site. A part of that was additionally reusing an animation I’d constructed circa 2019, the place a moon orbited across the planet.
Initially, I simply plopped the animations into the brand new web site, solely altering the models (em
models to viewport models utilizing some difficult math that I used to be very, very pleased with) in order that they might scale correctly as a result of I’m… environment friendly with my time. Nevertheless, on cell, the planet would transfer up a number of pixels and down a number of pixels because the moons orbited round it. I suspected the plopped-in animation was the offender (it wasn’t, however not less than I obtained some optimized animation and an article out of the deal).
Right here’s the unique animation:
My preliminary animation for the moon ran for 60 seconds. I’m folding it inside a disclosure widget as a result of, at 141 strains, it’s silly lengthy (and, as we’ll see, emphasis on the silly). Right here it’s in all its “glory”:
Open code
#moon1 {
animation: moon-one 60s infinite;
}
@keyframes moon-one {
0% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
5% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
9.9% {
z-index: 2;
}
10% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
15% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
19.9% {
z-index: -1;
}
20% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
25% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
29.9% {
z-index: 2;
}
30% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
35% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
39.9% {
z-index: -1;
}
40% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
45% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
49.9% {
z-index: 2;
}
50% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
55% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
59.9% {
z-index: -1;
}
60% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
65% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
69.9% {
z-index: 2;
}
70% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
75% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
79.9% {
z-index: -1;
}
80% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
85% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
89.9% {
z-index: 2;
}
90% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
95% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
99.9% {
z-index: -1;
}
100% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
}
In case you take a look at the keyframes in that code, you’ll discover that the 0%
to 20%
keyframes are precisely the identical as 20%
to 40%
and so forth up via 100%
. Why I made a decision to repeat the keyframes 5 occasions infinitely as a substitute of simply repeating one set infinitely is a call misplaced to antiquity, like six years in the past in internet time. We will additionally drop the length to 12 seconds (one-fifth of sixty) if we had been doing our due diligence.
I might thus delete all the pieces from 20%
on, immediately dropping the code right down to 36 strains. And sure, I notice features like this are unlikely to be attainable on most websites, however this is step one for optimizing issues.
#moon1 {
animation: moon-one 12s infinite;
}
@keyframes moon-one {
0% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
5% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
9.9% {
z-index: 2;
}
10% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
15% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
19.9% {
z-index: -1;
}
20% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
}
Now that we’ve gotten rid of 80% of the overwhelming bits, we are able to see that there are 5 principal keyframes and two extra ones that set the z-index
near the center and finish of the animation (these forestall the moon from dropping behind the planet or coming out from behind the planet too early). We will change these 5 factors from 0%
, 5%
, 10%
, 15%
, and 20%
to 0%
, 25%
, 50%
, 75%
, and 100%
(and for the reason that 0%
and the previous 20%
are the identical, we are able to take away that one, too). Additionally, for the reason that 10%
keyframe above is switching to 50%
, the 9.9%
keyframe can transfer to 49.9%
, and the 19.9%
keyframe can swap to 99.9%
, giving us this:
#moon1 {
animation: moon-one 12s infinite;
}
@keyframes moon-one {
0%, 100% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
25% {
remodel: translate(-3.51217391vw, 3.50608696vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
49.9% {
z-index: 2;
}
50% {
remodel: translate(-5.01043478vw, 6.511304348vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
75% {
remodel: translate(1.003478261vw, 2.50608696vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
99.9% {
z-index: -1;
}
}
Although I used to be very pleased with myself for my math wrangling, numbers like -3.51217391vw
are actually, actually pointless. If a display was one thousand pixels vast, -3.51217391vw
could be 35.1217391
pixels. Nobody ever must go right down to the precision of a ten-millionth of a pixel. So, let’s spherical all the pieces to the tenth place (and if it’s a 0
, we’ll simply drop it). We will additionally skip z-index
within the 75%
and 25%
keyframes because it doesn’t change.
Right here’s the place that will get us within the code:
#moon1 {
animation: moon-one 12s infinite;
}
@keyframes moon-one {
0%, 100% {
remodel: translate(0, 0) scale(1);
z-index: 2;
animation-timing-function: ease-in;
}
25% {
remodel: translate(-3.5vw, 3.5vw) scale(1.5);
z-index: 2;
animation-timing-function: ease-out;
}
49.9% {
z-index: 2;
}
50% {
remodel: translate(-5vw, 6.5vw) scale(1);
z-index: -1;
animation-timing-function: ease-in;
}
75% {
remodel: translate(1vw, 2.5vw) scale(0.25);
z-index: -1;
animation-timing-function: ease-out;
}
99.9% {
z-index: -1;
}
}
In spite of everything our adjustments, the animation nonetheless seems to be fairly near what it was earlier than, solely approach much less code:
One of many issues I don’t like about this animation is that the moon form of turns at its zenith when it crosses the planet. It will be a lot better if it traveled in a straight line from the higher proper to the decrease left. Nevertheless, we additionally want it to get a bit of bigger, as if the moon is coming nearer to us in its orbit. As a result of each translation and scaling had been accomplished within the remodel
property, I can’t translate and scale the moon independently.
If we skip both one within the remodel
property, it resets the one we skipped, so I’m compelled to guess the place the mid-point must be in order that I can set the dimensions I would like. A method I’ve solved this previously is so as to add a wrapping factor, then apply scale
to 1 factor and translate
to the opposite. Nevertheless, now that we’ve particular person scale
and translate
properties, a greater approach is to separate them from the remodel
property and use them as separate properties. Separating out the interpretation and scaling shouldn’t change something, until the unique order they had been declared on the remodel
property was totally different than the order of the singular properties.
#moon1 {
animation: moon-one 12s infinite;
}
@keyframes moon-one {
0%, 100% {
translate: 0 0;
scale: 1;
z-index: 2;
animation-timing-function: ease-in;
}
25% {
translate: -3.5vw 3.5vw;
z-index: 2;
animation-timing-function: ease-out;
}
49.9% {
z-index: 2;
}
50% {
translate: -5vw 6.5vw;
scale: 1;
z-index: -1;
animation-timing-function: ease-in;
}
75% {
translate: 1vw 2.5vw;
scale: 0.25;
animation-timing-function: ease-out;
}
99.9% {
z-index: -1;
}
}
Now that we are able to separate the scale
and translate
properties and use them independently, we are able to drop the translate
property within the 25%
and 75%
keyframes as a result of we don’t need them positioned exactly in that keyframe. We wish the browser’s interpolation to maintain that for us in order that it interprets easily whereas scaling.
#moon1 {
animation: moon-one 12s infinite;
}
@keyframes moon-one {
0%, 100% {
translate: 0 0;
scale: 1;
z-index: 2;
animation-timing-function: ease-in;
}
25% {
scale: 1.5;
animation-timing-function: ease-out;
}
49.9% {
z-index: 2;
}
50% {
translate: -5vw 6.5vw;
scale: 1;
z-index: -1;
animation-timing-function: ease-in;
}
75% {
scale: 0.25;
animation-timing-function: ease-out;
}
99.9% {
z-index: -1;
}
}
Lastly, these totally different timing features don’t make lots of sense anymore as a result of we’ve obtained the browser working for us, and if we use an ease-in-out
timing operate on all the pieces, then it ought to do precisely what we wish.
#moon1 {
animation: moon-one 12s infinite ease-in-out;
}
@keyframes moon-one {
0%, 100% {
translate: 0 0;
scale: 1;
z-index: 2;
}
25% {
scale: 1.5;
}
49.9% {
z-index: 2;
}
50% {
translate: -5vw 6.5vw;
scale: 1;
z-index: -1;
}
75% {
scale: 0.25;
}
99.9% {
z-index: -1;
}
}
And there you go: 141 strains down to twenty-eight, and I feel the animation seems to be even higher than earlier than. It is going to definitely be simpler to keep up, that’s for certain.
However what do you assume? Was there an optimization step I missed? Let me know within the feedback.