For this blog, I used the Hugo framework with the Risotto theme; I enjoy the minimalist style and this combination particularly convinced me, being simple and without too many frills.
I made some changes to the theme to adapt it to my needs, and the one I would like to talk about in this post concerns the animation of the logo background.
To achieve the rotating gradient effect that is currently visible, I initially experimented with a bit of JavaScript, like this:
document.addEventListener("DOMContentLoaded", () => {
const DEG_STEP = 2.8125 // equivalent to 360 / 128;
const INTERVAL = 62.5 // equivalent to 8000 / 128
let deg = 42;
let logo = document.querySelector(".page__logo-inner");
setInterval(() => {
deg += DEG_STEP;
deg %= 360;
logo.style["background-image"] = `linear-gradient(${deg}deg, rgb(247, 115, 241), rgb(16, 180, 215))`;
}, INTERVAL)
});
Upon page load, the setInterval function starts, modifying the background-image style every 62.5 milliseconds to effectively rotate the linear-gradient.
The loop lasts 8 seconds. In 8 seconds, 128 frames are drawn, and at each frame the rotation advances by 2.8125 degrees.
Everything worked perfectly, but I noticed a problem: when I opened the developer tools, the CPU usage spiked.
This happened because in the Elements tab of the developer tools, the page’s DOM is visible, with the inline styles clearly visible.
The style of the tag with the class .page__logo-inner, in fact, is constantly updated, and the browser consumes CPU to display the changes, which, as mentioned before, occur every 62.5 milliseconds.
Probably for 99.99% of users this does not represent a real problem, but I still wanted to try to solve it.
So, I decided to implement the same loop using CSS instead of JavaScript.
I initially tried to set 3 keyframes, like this:
.page__logo-inner {
background-image: linear-gradient(42deg, rgb(247, 115, 241), rgb(16, 180, 215));
animation: logo_anim 8s linear infinite;
}
@keyframes logo_anim {
0% {
background-image: linear-gradient(42deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
50% {
background-image: linear-gradient(222deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
100% {
background-image: linear-gradient(42deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
}
I expected the browser to calculate all the missing frames and make the animation smooth, but it didn’t work.
The animation, in fact, consisted of only 3 frames, without any interpolation, as you can see in the gif below.
As a result, it was necessary to explicitly write all 128 keyframes to obtain the same effect as with JavaScript. In this way, the browser would not have to perform any calculations, but only read the pre-calculated values.
But I had no intention of doing it by hand, so I wrote a small JS program to run directly on the browser console, which would produce the CSS rule with all the keyframes in text format, to be copied and pasted directly into the CSS file.
const STEPS = 128;
const START_DEG = 42;
const DEG_STEP = 2.8125;
let percent_step = 100 / STEPS;
let output = "@keyframes logo_anim {\n";
for (let i=0; i<STEPS; i++) {
let percent = i * percent_step;
let deg = (START_DEG + (i * DEG_STEP)) % 360;
output += `\t${percent.toFixed(2)}% {\n\t\tbackground-image: linear-gradient(${deg}deg, rgb(247, 115, 241), rgb(16, 180, 215));\n\t}\n`;
}
output += "}";
console.log(output);
After running it in the console, I simply copied the output and pasted it into the CSS file.
.page__logo-inner {
background-image: linear-gradient(42deg, rgb(247, 115, 241), rgb(16, 180, 215));
animation: logo_anim 8s linear infinite;
}
@keyframes logo_anim {
0.00% {
background-image: linear-gradient(42deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
0.78% {
background-image: linear-gradient(44.8125deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
1.56% {
background-image: linear-gradient(47.625deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
/* ... */
97.66% {
background-image: linear-gradient(33.5625deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
98.44% {
background-image: linear-gradient(36.375deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
99.22% {
background-image: linear-gradient(39.1875deg, rgb(247, 115, 241), rgb(16, 180, 215));
}
}
Problem solved! Now CPU usage remains low even when opening developer tools.