For creating a smooth 60fps transition, it's better to use requestAnimationFrame instead of setInterval.
By calling setInterval(change, 100), the background color will only be updated once every 100ms. Most devices refresh their screens 60 times a second. So, in order to get a smooth transition, you need to update the background color once every 16.67ms (1second/60). But, passing 16.67 to setInterval is not a reliable way to do this. This is where requestAnimationFrame comes in. When you put some code inside a requestAnimationFrame callback, the browser makes sure that that code gets executed just before the next repaint.
These are some great resources to learn more about this:
Window.requestAnimationFrame()
Rendering Performance
Optimize JavaScript Execution
Jake Archibald: In The Loop - JSConf.Asia
This recursive change function takes a callback and the value of red and calls itself using requestAnimationFrame till the value of red reaches 255. If you have passed a callback, it gets fired after the transition.
function change(callback, r) {
r = r === undefined ? 0 : r;
if (r >= 0 && r < 256) {
const color = `rgb(${r}, 0, 0)`;
requestAnimationFrame(() => {
document.body.style.backgroundColor = color;
change(callback, r + 1);
});
} else {
callback && callback();
}
}
change(() => {
alert('Transition complete');
});
Also, if you're using setInterval, don't forget to clear it using clearInterval once the transition is complete.
WindowOrWorkerGlobalScope.setInterval()
WindowOrWorkerGlobalScope.clearInterval()