javascriptanimationcss-animationsmutation-observersweb-animations

How to detect changes made by CSS3 animation or web animations API?


How can I detect changes made by CSS3 animations or web animations (Element.animate)??

(Sorry for my bad English! this is my first question in Stackoverflow)

I know about MutationObserver, it responds only if I change the inline style or if I use requestAnimationFrame (since I change the inline style using it). But if I use CSS3 animations or Web animations, MutationObserver doesn't respond since they don't change the inline style.

See this... There are two divs here... div1, div2. div1's position will change when div2's position changes. But this happens only if I use requestAnimationFrame as I said before.

My question is how can I do this for css3 animations and web animations (Element.animate)?

const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');

/***** Add mutation observer to detect change *****/

const mutation = new MutationObserver(mutations => {
  div1.style.left = div2.style.left;
});

mutation.observe(div2, {
  attributes: true
});

/***** Animation with css *****/

function cssAnimation() {
  div2.style.animation = 'anim 1.5s linear';
}

/***** Animation with web animations *****/

function webAnimation() {
  div2.animate({
    left: [0, '500px']
  }, {
    duration: 1500,
    easing: 'linear'
  });
}

/*****Animation with requestAnimationFrame ******/

// Current left position of div2
const left = 0;

function requestAnimation() {
  // Increase left position 5px per keyframe
  div2.style.left = `${(left += 5)}px`;

  // Increase left position until it reaches to 500px
  if (left < 500) {
    requestAnimationFrame(requestAnimation);
  }
}

function clearAnimations() {
  left = 0;
  div2.style.left = 0;
  div2.style.animation = 'unset';
}
@keyframes anim {
  from {
    left: 0;
  }
  to {
    left: 500px;
  }
}

#div1 {
  background: orange;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 200px;
}

#div2 {
  background: lightgreen;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 100px;
}
<div id="buttons">
  <h3>Animate with...</h3>
  <button onclick='cssAnimation()'>Css3</button>
  <button onclick="requestAnimation()">request animation frame</button>
  <button onclick="webAnimation()">web animations api</button>
  <button id="clear" onclick="clearAnimations()">Clear</button>
</div>

<div id="div1">
  Div1
</div>
<div id="div2">
  div2
</div>


Solution

  • You can use requestAnimationFrame and window.getComputedStyle() to get current animated styles during the animation, note, included fill:"forward" at Element.animate() call

    var div1 = document.getElementById("div1");
    var div2 = document.getElementById("div2");
    
    /***** Add mutation observer to detect change *****/
    
    var mutation = new MutationObserver(function(mutations) {
      div1.style.left = div2.style.left;
    });
    mutation.observe(div2, {
      attributes: true
    });
    
    /***** Animation with css *****/
    
    function cssAnimation() {
      div2.style.animation = "anim 1.5s linear forwards";
      let animationFrame;
    
      function getCurrentStyles() {
        console.log(window.getComputedStyle(div2).left);
        animationFrame = requestAnimationFrame(getCurrentStyles)
      }
      getCurrentStyles();
      div2.addEventListener("animationend", () => cancelAnimationFrame(animationFrame));
    }
    
    /***** Animation with web animations *****/
    
    function webAnimation() {
      let animationFrame;
    
      function getCurrentStyles() {
        console.log(window.getComputedStyle(div2).left);
        animationFrame = requestAnimationFrame(getCurrentStyles)
      }
      getCurrentStyles();
      div2.animate({
        left: [0, "500px"]
      }, {
        duration: 1500,
        fill: "forwards",
        easing: "linear"
      }).onfinish = function() {
        cancelAnimationFrame(animationFrame);
        console.log(window.getComputedStyle(div2).left);
      };
    }
    
    /*****Animation with requestAnimationFrame ******/
    
    //Current left position of div2
    var left = 0;
    
    function requestAnimation() {
      //Increase left position 5px per keyframe
      div2.style.left = `${(left += 5)}px`;
      console.log(window.getComputedStyle(div2).left);
      //Increase left position until it reaches to 500px
      if (left < 500) {
        requestAnimationFrame(requestAnimation);
      }
    }
    
    function clearAnimations() {
      left = 0;
      div2.style.left = 0;
      div2.style.animation = "unset";
    }
    @keyframes anim {
      from {
        left: 0;
      }
      to {
        left: 500px;
      }
    }
    
    #div1 {
      background: orange;
      width: 100px;
      height: 100px;
      position: absolute;
      top: 200px;
    }
    
    #div2 {
      background: lightgreen;
      width: 100px;
      height: 100px;
      position: absolute;
      top: 100px;
    }
    <div id="buttons">
      <h3>Animate with...</h3>
      <button onclick='cssAnimation()'>Css3</button>
      <button onclick="requestAnimation()">request animation frame</button>
      <button onclick="webAnimation()">web animations api</button>
      <button id="clear" onclick="clearAnimations()">Clear</button>
    </div>
    
    <div id="div1">
      Div1
    </div>
    <div id="div2">
      div2
    </div>