Search code examples
javascriptcssanimationrequestanimationframe

Why is window.requestAnimationFrame not working immediately after page refresh?


In a project I'm coding, I am using window.requestAnimationFrame in order to make an animation repeat each time a button is clicked. I'm very confused though because after I refresh the page, if I click the button two times, the second time the animation doesn't happen. It only works on the second time if I click something else on the page before clicking the button, but if I click it immediately after refreshing, it doesn't. Here's some of the code from the button when it is clicked, but if you need more code, I can provide it. Thanks in advance for the help!

submitNewPlunderForm.onclick = () => {
    let users = JSON.parse(localStorage.users);
    let username = newPlunderFormUsername.value;
    if (users[username] === undefined) {
        newPlunderFormUsername.style.border = "1.5px solid #eb3a34";
        newPlunderFormUsername.style.boxShadow = "none";
        newPlunderFormUsername.style.transition = "none"; 
        window.requestAnimationFrame(function() {
            newPlunderFormUsername.style.transition = "box-shadow 200ms";
            newPlunderFormUsername.style.boxShadow = "0 0 5px #eb3a34"; 
        }); 

Solution

  • When the first rAF callback fires after page load varies a lot across implementations, and there isn't really any specs about this, the Document has to be fully active before the callbacks can fire, but browsers can add whatever delay they want.
    From memory, in Firefox it may take about 20ms after the call to requestAnimationFrame even with a 60FPS monitor (that is it waits about two frames), while on Chrome it may happen almost synchronously.

    Also, most modern browsers do throttle click events to the animated-frames rate, so your two calls may happen in the same event-loop frame, before the browser triggers any reflow.
    Finally, Chrome is very buggy.

    But anyway, for what you are doing, you don't need rAF at all, just trigger a reflow synchronously:

    submitNewPlunderForm.onclick = () => {
        let users = JSON.parse(localStorage.users);
        let username = newPlunderFormUsername.value;
        if (users[username] === undefined) {
            newPlunderFormUsername.style.border = "1.5px solid #eb3a34";
            newPlunderFormUsername.style.boxShadow = "none";
            newPlunderFormUsername.style.transition = "box-shadow 200ms";
            newPlunderFormUsername.offsetWidth; // force reflow
            newPlunderFormUsername.style.boxShadow = "0 0 5px #eb3a34";
        }
    });
    

    const newPlunderFormUsername = document.createElement("input");
    document.body.append(newPlunderFormUsername);
    newPlunderFormUsername.style.boxShadow = "none";
    newPlunderFormUsername.style.transition = "box-shadow 2000ms";
    newPlunderFormUsername.offsetWidth; // force reflow
    newPlunderFormUsername.style.boxShadow = "0 0 5px #eb3a34";