web-animations / web-animations-js-legacy

The original emulator of the Web Animations specification. Please use web-animations-js instead:
https://github.com/web-animations/web-animations-js
Apache License 2.0
706 stars 84 forks source link

Any way to apply a WebAnimation *after* the element's style? #629

Closed joeytwiddle closed 10 years ago

joeytwiddle commented 10 years ago

For example, I have an element with some arbitrary transforms already, in this case a rotation and a scaling:

//element.style.transform = "rotate(45deg) scale(0.5)";

// Correction: I originally posted rotate(45deg) scale(0.5) but actually the issue is with
element.style.transform = "matrix(0.3536, 0.3536, -0.3536, 0.3536, 0, 0)";

Now I want to animate this element to move horizontally. So I try:

dt.play(new Animation(element, new KeyframeEffect([
  {transform: "translate(0px, 0px)"},
  {transform: "translate(200px, 0px)"}
], "add"), timing));

But the result is that the translation is applied first, and then the translation is rotated and scaled by the element's own transform. (In this case, the element moves 100 pixels along the diagonal, although I wanted it to move 200 pixels along the horizontal axis.)

Is there any way to prioritize the animation so that the translation is applied after the element's transform?

(I do have a workaround, which is to pull the element's transform into a String, reset it, and then add that transform after the translate, either in the given Animation, or as a second Animation. But this is not really very clean!)

shans commented 10 years ago

This seems like a bug to me. @alancutter, want to take a look?

alancutter commented 10 years ago

I just tried this and it works as expected for me (animation applying on top of existing inline style).

<!DOCTYPE html>
<script src="web-animations.js"></script>
<div id="target">target</div>
<script>
target.style.width = '100px';
target.style.transform = 'rotate(45deg)';
target.animate(new KeyframeEffect([
  {transform: 'translateX(0px)'},
  {transform: 'translateX(100px)'},
], 'add'), 1000);
</script>

@joeytwiddle can you provide a full code example of the problem?

joeytwiddle commented 10 years ago

Ah, my apologies, my problem is not with rotate(45deg). It occurs when I use:

target.style.transform = 'matrix(0.7071, 0.7071, -0.7071, 0.7071, 0, 0)';

That is enough to reproduce the issue under Firefox 30.0 and 31.0.

I am not sure if matrices are supposed to work differently by the spec, or if they are just not fully supported in the polyfill. Hopefully it's the latter, then I can wait for native or -next, or try to fix it.

Aside: Your example works for me in Firefox. But in Chromium 34 I see no rotation until I swap .transform for ['-webkit-transform'] and then I see the bug! Hopefully that will disappear when I upgrade...

alancutter commented 10 years ago

I've updated the test case to use a matrix, it still behaves as expected in Firefox 31. Can you provide a concrete code example of the problem?

<!DOCTYPE html>
<script src="web-animations.js"></script>
<div id="target">target</div>
<script>
target.style.width = '100px';
target.style.transform = 'matrix(0.7071, 0.7071, -0.7071, 0.7071, 0, 0)';
target.animate(new KeyframeEffect([
  {transform: 'translateX(0px)'},
  {transform: 'translateX(100px)'},
], 'add'), 1000);
</script>
alancutter commented 10 years ago

I just re-read the original post. I think the issue is in your expectations of transform composition. The animation is correctly applying the translation after the rotation, the rotation has affected the co-ordinate system other transforms operate in and causes a translation in X to appear diagonal rather than horizontal.

You can confirm this behaviour by removing the animation and setting the inline style directly: target.style.transform = 'rotate(45deg) translateX(100px)';}

joeytwiddle commented 10 years ago

Thanks, my test case matches the test case you just posted.

There was indeed some confusion with my understanding of how composition works with/without matrices. I was sometimes seeing different behaviour between rotate and matrix transforms, but in Chrome 36 the behaviour is consistent. Another difficulty arises when we talk about this: adding an animation after all the others results in that transformation being applied before all the others!

But going back to the original question, is there any way to achieve what I want? Namely, to perform the translation independently from the matrix on the element's style. (Queue the animation "before" the element.style, so that its transformation is applied last.) I notice the spec talks about prioritization of animations, but I don't see any way for the user to control it.

I can adopt one of the workarounds if there is no neater solution.

joeytwiddle commented 10 years ago

There is a simpler workaround, which is to create a container for the rotated div, and set the translation on the container. (I was just hoping to do it without a wrapper div.)

alancutter commented 10 years ago

There's no control over ordering animations with respect to inline style; animations always apply after inline style. For ordering of animations amongst themselves one straightforward way to control it is by ordering the children in an AnimationGroup in the order you want them to apply their effects.

joeytwiddle commented 10 years ago

Thank you for the clarification!