3

I have a CSS animation that uses transforms to move around relative to its current position.

CSS

fish {
    animation: fishanimation 4s ease-in-out infinite alternate;
}

@keyframes fishanimation {
  0% {
    transform: translateY(20px);
  }

  100% {
    transform: translateY(80px);
  }
}

Now I want to adjust the scale and rotation dynamically using javascript, but this gets overwritten by the running css animation. The transforms are not combined :(

Javascript

this.div.style.transform = "scale(0.4) rotate(45deg)";

Is it possible to have the scale and rotation apply to the already existing transformation? Something like:

element.transform = element.transform + "scale(0.4) rotate(34deg)";

EDIT:

This is not a duplicate of the question: how to combine css transforms. The point of this question is that an element may already have a transform that I don't want to overwrite. I want to add to it, without actually knowing what the current transform state is.

Community
  • 1
  • 1
Kokodoko
  • 26,167
  • 33
  • 120
  • 197
  • Do you mean to say you want to add on to the transform as the animation is running? – Harry Apr 13 '16 at 12:56
  • 1
    Yes, the existing animation that I defined in the stylesheet should keep running. I want to change the transforms that aren't yet defined in the css, such as scale and rotation. – Kokodoko Apr 13 '16 at 12:57
  • 2
    Once an animation starts and is running, the animation takes control of the properties that are part of the keyframes and so, no, you can't edit it via inline styles. You'd have to rewrite the keyframes and refire the animation. You would find it mentioned in the spec [here](https://www.w3.org/TR/css3-animations/#animations) (first para under Section 3). – Harry Apr 13 '16 at 12:58
  • 1
    May I recommend a library to use, Velocity JS, http://julian.com/research/velocity/#transforms – Shakespeare Apr 13 '16 at 12:59
  • You can find a sample for dynamically creating and applying animations in this answer - http://stackoverflow.com/questions/35166454/dynamically-create-and-apply-css3-animations/35240448#35240448 – Harry Apr 13 '16 at 13:04
  • @OwenAyres thanks that looks outstanding! – Kokodoko Apr 13 '16 at 13:15
  • @Harry I didn't know that was possible! But you'll still overwrite the entire animation, I just want to add transforms to an animation that's already running without interrupting it. I suppose I could add a sub container div and animate that. – Kokodoko Apr 13 '16 at 13:15
  • @Kokodoko: As I pointed out in my first comment, what you are looking for is impossible with CSS animations. You could ofcourse add a container and animate it like you mentioned. – Harry Apr 13 '16 at 13:16
  • Possible duplicate of [How to apply multiple transforms in CSS?](http://stackoverflow.com/questions/10765755/how-to-apply-multiple-transforms-in-css) – Rob Apr 13 '16 at 20:22

1 Answers1

4

As commented, I recommend you use an animation library for this, a good example is Velocity JS.

If a library like suggested is not an option, then read on!

If you were looking to do multiple transforms like that in one on top of your previous transforms, then you'd need to use the matrix value for the transform. Things get a little complicated with the matrix. The parameters for the matrix are in this order: -

matrix(scaleX(),skewY(),skewX(),scaleY(),translateX(),translateY());

Let's use the example of starting off with the initial fish animation you mentioned: -

fish {
    animation: fishanimation 4s ease-in-out infinite alternate;
}

@keyframes fishanimation {
  0% {
    transform: matrix(1, 0, 0, 1, 0, 20);
  }

  100% {
    transform: matrix(1, 0, 0, 1, 0, 80);
  }
}

To dynamically change this in JavaScript will require some unpleasant string manipulation and getting the transform property of the element (which is more awkward than you think). Here's an example of how you might do it.

function getTransformValues(obj) {
    var transform = {
        scaleX: 1,
        skewX: 0,
        skewY: 0,
        scaleY: 1,
        translateX: 0,
        translateY: 0
    };

    var matrix = obj.css("-webkit-transform") ||
                 obj.css("-moz-transform")    ||
                 obj.css("-ms-transform")     ||
                 obj.css("-o-transform")      ||
                 obj.css("transform");

    if (matrix && matrix !== 'none') {
        var values = matrix.split('(')[1].split(')')[0].split(',');
        transform.scaleX = parseFloat(values[0]);
        transform.skewY = parseFloat(values[1]);
        transform.skewX = parseFloat(values[2]);
        transform.scaleY = parseFloat(values[3]);
        transform.translateX = parseFloat(values[4]);
        transform.translateY = parseFloat(values[5]);
    }

    return transform;
}

var values = getTransformValues($('#test'));
console.log(values);
// Object {scaleX: 1, skewX: 0, skewY: 0, scaleY: 2, translateX: 50, translateY: 40}

Now you've got those values you can start creating a new matrix by only changing the one you want, for example: -

var old = {
    scaleX: 1,
    skewX: 0,
    skewY: 0,
    scaleY: 2,
    translateX: 50,
    translateY: 40
};

$('#test').css('transform', 'matrix(' + old.scaleX + ', ' + old.skewY + ', ' + old.skewX + ', ' + old.scaleY + ', ' + old.translateX + ', ' + old.translateY + ')');

If you want to start manipulating the rotation angle things get even more complicated. To avoid bogging this answer so much, this part of the question has a solution at the following link: Get element -moz-transform:rotate value in jQuery

Hopefully you can see why adopting libraries such as Velocity JS, at least at the time of writing, is the best way to go for quick, easy, clean and smooth cross-browser animation.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Shakespeare
  • 1,286
  • 9
  • 15
  • Thanks for the elaborate explanation! – Kokodoko Apr 14 '16 at 15:39
  • Note that the `parseInt()` may not be a good approach if the transform has rotation applied (and you want to preserve the actual transform - even if you're **not** wanting to edit the rotation). For example, if I have a `transform: rotate(3deg)` applied, and then check its value via `$element.css("transform")`, then we get `"matrix(0.99863, 0.052336, -0.052336, 0.99863, 0, 0)"`. Apparently the rotation transform makes floating-point values, even for the `skew` aspects of the matrix. So we might have better luck using something like `parseFloat()` in some cases. – Jason Frank Mar 30 '17 at 16:12
  • Another edit that needs done is in the `getTransformValues()` function. The `skewY` value comes from `values[1]` and the `skewX` value comes from `values[2]`. As my previous comment indicated, if you have a `rotate` transform, then those `skew` values will get populated, so if you have the `skewX` and `skewY` getting the wrong value, the rotation will get flip-flopped. Notice that order of `skewY()` and `skewX()` was stated earlier in the answer. – Jason Frank Mar 30 '17 at 16:58