javascriptcssanimationcss-animationsweb-animations

Way to determine the exact style properties currently being applied by a web animation (without being mixed in with all styles currently applied)


I'm working on a project where I'd like to be able to determine exactly what style properties an animation is currently applying to an element, and ideally only those changes imparted by the animation.

Given the example below, I'd want to be able to know exactly how the scale and rotate properties are set no matter where I paused the animation without having to sort through inherent properties like padding or the blue color imparted by the other button.

If this simply isn't easily doable that's fine, I'd just like confirmation of that being the case. If so I'm open to brainstorming reasonable alternatives.

What I've considered:

I've seen some quality examples using getComputedStyle() and I think that's fairly close to what I want but ideally I'd like an object or css string that only contains the style properties that are being applied by the animation (so no baseline style params that come from a css file or applied by other means).

I've looked through the web animation API docs pretty extensively and I haven't found anything to directly address my needs. A reasonable naive approach is to look at the first or last (depending on animation direction) keyFrameEffect object in the animation and inspect the property value pairs for relevant properties but with how many ways animations can be paused or end at in between / unexpected spots in the animation cycle this doesn't seem like a thorough solution. For example, this would not work if the animation is paused or even in a finish state if a value such as iterationStart is set since it now offsets the finish state away from the final keyframe.

Perhaps there's a way to take the animation object and apply its exact current state to an otherwise blank object via commitStyles() or something so the resulting styles object on the blank element is contains only properties applied by the animation? I worry about custom elements / properties but maybe I'm overthinking it.

No matter what it's critical I don't directly change the element being animated or any of its properties so I'd need to be careful and make sure the commitStyles only affected the blank object. Possibly by changing the target of the keyframes then setting it back when finished?

Thanks in advance for any suggestions!

Example:

const newspaper = document.querySelector(".newspaper");
const colorChange = document.querySelector(".color-change");
const pauseButton = document.querySelector(".pause");
const finish = document.querySelector(".finish");

// I want a way to directly get the current value of these properties 
// without being mixed in with other non animation related css properties
const newspaperSpinning = [
  { transform: "rotate(0) scale(1)" },
  { transform: "rotate(360deg) scale(0.7)" },
];

const newspaperTiming = {
  duration: 2000,
  iterations: 999,
  fill: 'forwards',
  iterationStart: 0.3
};

const newspaperKeyFrames = new KeyframeEffect(
  newspaper, // element to animate
  newspaperSpinning,
  newspaperTiming, // keyframe options
);

const animation = new Animation(
  newspaperKeyFrames,
  document.timeline,
);


newspaper.addEventListener("click", () => {
  animation.play()
});

colorChange.addEventListener("click", () => {
  newspaper.style.color = 'blue';
});

pauseButton.addEventListener("click", () => {
  animation.pause();
});

finish.addEventListener("click", () => {
  animation.finish();
});
html,
body {
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: black;
}

button {
  cursor: pointer;
}

.newspaper {
  padding: 0.5rem;
  text-transform: uppercase;
  text-align: center;
  background-color: white;
  cursor: pointer;
}
<div class="newspaper">click to start spinning newspaper animation</div>
<button class="color-change">click to change the color</button>
<button class="pause">click to pause the animation</button>
<button class="finish">click to finish the animation</button>

Note: if there are multiple animations currently acting on an element it's fine and maybe even preferred if the result is a list / object of all style properties actively imparted by any animations on the element.


Solution

  • editor of the Web Animations spec here. To answer your question simply, it's not possible. We really should add this API (and it has been proposed before).

    The best you can do at the moment is use getComputedStyle. However, note that getComputedStyle serializes transform values as a matrix() (spec reference) so you cannot easily inspect the individual scale and rotation components. One way you can work around that is to animate the scale and rotate properties which are now implemented in all browsers as of Baseline 2022.

    A similar issue is true not just for transforms but for all properties since animation interpolation happens in computed value space. For example, when animating between 1em and 20px you will get back a result in px.

    In order to ignore the effect of other animations or styles on the target element, like you say, you can change the target of the KeyframeEffect to another element whose rotate and scale properties are set to initial or none (e.g. using a declaration like all: initial) and then set it back when you are done reading getComputedStyle.

    As an alternative to modifying the target, it is generally quite easy to duplicate animations and their effects. You could create a clone of the KeyframeEffect and a new Animation and seek the Animation to the same time as the one you which to inspect by setting its currentTime. (Unlike calling play(), which is asynchronous, clone.currentTime = source.currentTime is synchronous.)

    If you don't know which properties are being animated by an animation, then you can iterate over the keyframes and collect all the keys that are not offset, composite or easing in a Set.

    Note that animations can add together and can add to styles on the target element so you'd need to consider if you want the combined result (by pointing all your animations at your mirror element) or the individual contribution of each animation.

    For what it's worth, the Firefox animation DevTools take this approach of creating a local copy of the animation and sampling its values at various points in order to produce its graphs of animation effects (source here).