Created
November 5, 2024 08:47
-
-
Save chenglou/8af5467b47dd4cf58745cfe950bec13f to your computer and use it in GitHub Desktop.
This file contains 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
* { | |
font-family: monospace; | |
} | |
.box { | |
position: absolute; | |
border-radius: 8px; | |
} | |
/* Add new styles for strips */ | |
.horizontal-strip { | |
width: 300px; | |
height: 40px; | |
left: 150px; | |
} | |
.vertical-strip { | |
width: 40px; | |
height: 200px; | |
} | |
.horizontal-label { | |
-webkit-user-select: none; | |
user-select: none; | |
position: absolute; | |
right: 100%; | |
top: 50%; | |
transform: translateY(-50%); | |
margin-right: 10px; | |
} | |
.vertical-label { | |
-webkit-user-select: none; | |
user-select: none; | |
position: absolute; | |
bottom: -25px; | |
left: 50%; | |
transform: translateX(-50%); | |
white-space: nowrap; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="strip1" class="box horizontal-strip" style="top: 100px;"> | |
<span class="horizontal-label">Baseline</span> | |
</div> | |
<div id="strip2" class="box vertical-strip" style="left: 200px; top: 20px;"> | |
<span class="vertical-label">1x raf</span> | |
</div> | |
<div id="strip3" class="box vertical-strip" style="left: 280px; top: 20px;"> | |
<span class="vertical-label">2x raf</span> | |
</div> | |
<div id="strip4" class="box vertical-strip" style="left: 360px; top: 20px;"> | |
<span class="vertical-label">7x raf</span> | |
</div> | |
<div style="position: relative; top: 260px; | |
background: rgba(0, 0, 0, 0.8); padding: 16px; border-radius: 8px; color: white; | |
font-size: 14px; line-height: 1.5; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);"> | |
<div id="display1">1x raf delay vs baseline: -</div> | |
<div id="display2">2x raf delay vs baseline: -</div> | |
<div id="display3">7x raf delay vs baseline: -</div> | |
<p>Here are 4 window click event handlers, 1 per strip. On click:</p> | |
<li>The horizontal strip sets its color directly.</li> | |
<li>The 1st vertical strip sets its color inside a requestAnimationFrame (raf) callback.</li> | |
<li>The 2nd vertical strip sets its color inside a raf, inside another raf.</li> | |
<li>The 3rd vertical strip sets its color within 7 layers of nested rafs.</li> | |
<p>Per <a href="https://medium.com/@paul_irish/requestanimationframe-scheduling-for-nerds-9c57f7438ef4" | |
style="color: white;">Paul Irish's article</a>,</p> | |
<li>Logic inside a single raf inside an event handler is guaranteed to run in the same frame as the event handler. | |
Which means the horizontal strip and the 1st vertical strip will <b>always</b> change color at the same time. | |
</li> | |
<li>Logic inside a raf wrapped with another raf is guaranteed (?) to run the next frame vs the event handler. | |
Which means the 2nd vertical strip should <b>always</b> change color slightly after the baseline (and thus 1st | |
vertical strip). | |
</li> | |
<p>However, we can observe that the 2nd vertical strip sometime changes color <b>at the same time</b> as the | |
horizontal and 1st vertical strip.</p> | |
<p>So either the callback inside a raf inside another raf (2nd vertical strip) is sometime not actually postponed to | |
the next frame | |
(rather, executed on the same frame as the event handler), or there's a missing browser repaint in-between frames. | |
We do see that 7x raf logic is properly postponed to... some frame. | |
</p> | |
<p>The ramifications of this test is either some hard-to-fix animation janks, or bad data race conditions.</p> | |
<p>Please ping <a href="https://x.com/_chenglou" style="color: white;">@_chenglou</a> if you have any insight! | |
Thanks</p> | |
</div> | |
<script> | |
const strip1 = document.getElementById('strip1') | |
const strip2 = document.getElementById('strip2') | |
const strip3 = document.getElementById('strip3') | |
const strip4 = document.getElementById('strip4') | |
const display1 = document.getElementById('display1') | |
const display2 = document.getElementById('display2') | |
const display3 = document.getElementById('display3') | |
strip1.style.background = strip2.style.background = strip3.style.background = strip4.style.background = '#4ECDC4' | |
let strip1State = 0, strip2State = 0, strip3State = 0, strip4State = 0 | |
function changeColor(element, boxState) { | |
const colors = ['#FF6B6B', '#4ECDC4'] | |
element.style.background = colors[boxState % colors.length] | |
} | |
let time1 | |
window.addEventListener('click', () => { | |
time1 = performance.now() | |
changeColor(strip1, strip1State) | |
strip1State++ | |
}) | |
window.addEventListener('click', () => { | |
requestAnimationFrame((time2) => { | |
display1.innerText = `1x raf delay vs baseline: ${(time2 - time1).toFixed(1)}ms` | |
changeColor(strip2, strip2State) | |
strip2State++ | |
}) | |
}) | |
window.addEventListener('click', () => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame((time3) => { | |
display2.innerText = `2x raf delay vs baseline: ${(time3 - time1).toFixed(1)}ms` | |
changeColor(strip3, strip3State) | |
strip3State++ | |
}) | |
}) | |
}) | |
window.addEventListener('click', () => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame(() => { | |
requestAnimationFrame((time4) => { | |
display3.innerText = `7x raf delay vs baseline: ${(time4 - time1).toFixed(1)}ms` | |
changeColor(strip4, strip4State) | |
strip4State++ | |
}) | |
}) | |
}) | |
}) | |
}) | |
}) | |
}) | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks nomsternom