Last active
October 3, 2025 15:53
-
-
Save idettman/99584f43d0c5b3f72fc1e3db2c3b8fa6 to your computer and use it in GitHub Desktop.
HTML Animation Performance Test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const boxes = []; | |
| const button = document.getElementById("toggle-button"); | |
| const boxContainer = document.getElementById("box-container"); | |
| const animationType = document.getElementById("type"); | |
| // create boxes | |
| for (let i = 0; i < 1000; i++) { | |
| const div = document.createElement("div"); | |
| div.classList.add("css-animation"); | |
| div.classList.add("box"); | |
| boxContainer.appendChild(div); | |
| boxes.push(div.style); | |
| } | |
| let toggleStatus = true; | |
| let rafId; | |
| button.addEventListener("click", () => { | |
| if (toggleStatus) { | |
| animationType.textContent = " requestAnimationFrame"; | |
| for (const child of boxContainer.children) { | |
| child.classList.remove("css-animation"); | |
| } | |
| rafId = window.requestAnimationFrame(animate); | |
| } else { | |
| window.cancelAnimationFrame(rafId); | |
| animationType.textContent = " CSS animation"; | |
| for (const child of boxContainer.children) { | |
| child.classList.add("css-animation"); | |
| } | |
| } | |
| toggleStatus = !toggleStatus; | |
| }); | |
| const duration = 6000; | |
| const translateX = 500; | |
| const rotate = 360; | |
| const scale = 1.4 - 0.6; | |
| let start; | |
| function animate(time) { | |
| if (!start) { | |
| start = time; | |
| rafId = window.requestAnimationFrame(animate); | |
| return; | |
| } | |
| const progress = (time - start) / duration; | |
| if (progress < 2) { | |
| let x = progress * translateX; | |
| let transform; | |
| if (progress >= 1) { | |
| x = (2 - progress) * translateX; | |
| transform = `translateX(${x}px) rotate(${ | |
| (2 - progress) * rotate | |
| }deg) scale(${0.6 + (2 - progress) * scale})`; | |
| } else { | |
| transform = `translateX(${x}px) rotate(${progress * rotate}deg) scale(${ | |
| 0.6 + progress * scale | |
| })`; | |
| } | |
| for (const box of boxes) { | |
| box.transform = transform; | |
| } | |
| } else { | |
| start = null; | |
| } | |
| rafId = window.requestAnimationFrame(animate); | |
| } | |
| /* | |
| The animation can be switched to requestAnimationFrame() by clicking the toggle button. | |
| Try running them both now, comparing the FPS for each (the first purple box.) You should see that the performance of CSS animations and requestAnimationFrame() are very close. | |
| Off main thread animation | |
| Even given the test results above, we'd argue that CSS animations are the better choice. | |
| But how? | |
| The key is that as long as the properties we want to animate do not trigger reflow/repaint (read CSS triggers for more information), | |
| we can move those sampling operations out of the main thread. | |
| Off the main thread compositing: https://wiki.mozilla.org/Platform/GFX/OffMainThreadCompositing | |
| The most common property is the CSS transform. | |
| If an element is promoted as a layer, animating transform properties can be done in the GPU. | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment