Place by time, not by frame: three screens, then the real loop
The simulated lines are arithmetic, so they are the same on every machine. The number of real frames is not logged: it depends on the screen. Only the end position is, and it does not.
Everything that moves on a web page is drawn in frames. requestAnimationFrame and d3.timer call your function once per frame, and your function should place things by elapsed time, not by counting frames. Respect prefers-reduced-motion, and stop loops when the page is hidden.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 4 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write animate(duration, draw) with d3.timer: it calls draw(k) with k from 0 to 1 by elapsed time, stops itself at 1 and returns a promise. Why is a promise useful here?
Write motion(ms) that returns 0 when reduced motion is requested and ms otherwise, and use it in bar.transition().duration(motion(300)). What happens with a duration of 0?
Explain it without notes
Why should an animation be placed by elapsed time and not by counting frames?
What is the difference between requestAnimationFrame and setInterval(fn, 16)?
What does prefers-reduced-motion: reduce mean, and what should a chart do about it?
Preview
Console
Nothing logged yet.Expected console output
30 Hz | per-frame: 150 px | per-time: 300 px | frame budget: 33.3 ms
60 Hz | per-frame: 300 px | per-time: 300 px | frame budget: 16.7 ms
144 Hz | per-frame: 720 px | per-time: 300 px | frame budget: 6.9 ms
loop stopped itself, dot at 320 | drew more than one frame: true