36

When we use CSS3 transform: operation1(...) operation2(...), which one is done first?

The first operation done seems to be the one the most on the right., i.e. here operation2 is done before operation1. Just to be sure, is it true?

Note: I have read one thing and its contrary in some places (answers, articles on the internet), thus the question here.

Basj
  • 41,386
  • 99
  • 383
  • 673
  • Since all your examples are consistent whith what you think, and with the documentation, why are you doubting ? – vals Dec 24 '14 at 14:28
  • 1
    it is from left to right. check [this](https://drafts.csswg.org/css-transforms-1/#transform-rendering) – vadasambar Jun 23 '17 at 14:44
  • Let's end this confusion once and for all (after reading other posts below with all their comments). In math, the order is also reversed, no matter how cleverly some guys say a(b(c(...))) - yes, in this case, the order of operations is still c, b, and then a, you just read it out loud as a, b, and then c. Innermost bracket first. Now, since CSS uses no brackets to encapsulate each transformation, it just says "a b c", sure it's pretty confusing that in fact it means "a(b(c(...)))", but that's what it is, therefore, c happens first. IN PRACTICE, they happen right to left. End of story. – dkellner Sep 14 '20 at 20:27

5 Answers5

45

Yes, the first operation done is the one the most on the right., i.e. here operation2 is done before operation1.

This MDN article states indeed:

The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied in order from right to left.

Here is the documentation : http://www.w3.org/TR/css-transforms-1/.


Example 1

Here the scaling is done first, and then the translation of 100px vertically (if translation was done first, the scaling would make the translation of 500px!)

#container { 
   position: absolute; 
   transform: translate(0,100px) scale(5); 
   transform-origin: 0 0; }
<div id="container"><img src="https://i.stack.imgur.com/xb47Y.jpg"></img></div>

Example 2

Here the translation is done first, and then the scaling (the scaling done after makes that the translation looks like a 500px-translation!)

#container { 
   position: absolute; 
   transform: scale(5) translate(0,100px); 
   transform-origin: 0 0; }
<div id="container"><img src="https://i.stack.imgur.com/xb47Y.jpg"></img></div>
Basj
  • 41,386
  • 99
  • 383
  • 673
  • 2
    This answer is wrong. It is done from left to right as the other answer states. This answer should be removed. – T3rm1 Aug 03 '17 at 11:12
  • 4
    @T3rm1 Please run the code snippets, you will see by yourself that it's true. – Basj Aug 03 '17 at 14:08
  • 1
    This should be removed or updated to say left to right. The spec says itself that transformations are applied left to right: https://drafts.csswg.org/css-transforms-1/#transform-rendering – Jeff Klein Oct 12 '17 at 01:02
  • Please provide an answer @JeffreyKlein with a runnable code snippet so that we can all test your claim in various browsers. Also can you cite the sentence from your link that says this? – Basj Oct 12 '17 at 05:43
  • "3. Multiply by each of the transform functions in transform property from left to right" – Jeff Klein Oct 12 '17 at 19:50
  • 4
    @JeffreyKlein If you run my example 1, you will see that `scale(5)` is run *first*. Then it is translated of 100px to the south. And not the contrary. If it was the contrary, i.e. translation first, and then scaling x5, then we would see 100*5 = 500px white on top. It's not the case. **So I confirm it is right to left**, at least according to the math definitions of plane transformations and composition of such operations. If you disagree, please provide another answer with a runnable code snippet, so we can test what you suggest. – Basj Oct 12 '17 at 20:43
  • Did you miss the part where I provided the official CSS specification? – Jeff Klein Oct 13 '17 at 20:43
  • 2
    @JeffreyKlein Ok I see your confusion now. In fact the sentence you are referring confirms my answer ;) See a linear algebra course and [composition](https://en.wikipedia.org/wiki/Function_composition) of functions (it's the same about matrix seen as transformation function on the plane). In short here is the idea: when you write `(g ∘ f)(x) = g(f(x))`, even if g is on the left, the first function that you apply on the input (x) is f, and then g. – Basj Oct 13 '17 at 21:13
  • 1
    It seems the confusion here is that function composition order and sequential order of operations are exact opposites of each other. **Sequentially,** CSS transforms are performed right to left. If you're not convinced, try `translateX(100%) rotate(90deg)` vs `rotate(90deg) translateX(100%)` and see whether the element ends up to the right or below its original position. – Chris Calo Dec 04 '18 at 16:46
  • 4
    Also just noticed this line in [the MDN article on CSS Transforms](https://developer.mozilla.org/en-US/docs/Web/CSS/transform): "The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied in order from right to left." – Chris Calo Dec 05 '18 at 15:55
  • Your apple image is first translated 100px down then scaled 5 times. Here the operation started from left to right. If you are still in doubt, test `translateX rotate` and `rotate translateX` you will see the point more clearly – hoangfin May 05 '20 at 18:31
  • @meteorzero With a mathematic/geometry point of view, your assertion is wrong. Let's taxe `x=(0,0)`. If you do a [translation](https://en.wikipedia.org/wiki/Translation_(geometry)) of (0,100) called `f` first, and then a [homothetic transformation](https://en.wikipedia.org/wiki/Homothetic_transformation) of factor 5 called `g`, then `(g o f)(x) = g(f(x))` would be `(0,500)`. You will see that **it's not the case here**. Here we are doing `f(g(x))`, i.e. `(0, 100)`. You can see here that top left corner of the apple has coords (0,100). See: https://en.wikipedia.org/wiki/Geometric_transformation – Basj May 05 '20 at 18:41
  • @Basj scroll down and read `skewing and translating` from MDN https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transforms/Using_CSS_transforms. It clearly says `Here is the MDN logo, skewed by 10 degrees and translated by 150 pixels on the X-axis.` – hoangfin May 05 '20 at 21:07
  • @Basj I care more about the final result of transformation rather than your geometry theory with f(x) and g(x) – hoangfin May 05 '20 at 21:09
  • @meteorzero If you want to make an assertion "the transformations are done in order ... and ...", then you are speaking about geometric transformations, and then you need to use math conventions. If not, it's like speaking about nothing and just hand-waving. If you prefer an argument from authority (rather than mine, which I can understand!) to close this question: https://developer.mozilla.org/en-US/docs/Web/CSS/transform: *"The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied **in order from right to left**"*. – Basj May 05 '20 at 22:28
10

This has been mentioned in other answers and comments, but not with enough emphasis in my opinion: the short answer is both ways are valid.

It all depends whether you consider your coordinates attached to your element (left to right) or fixed to the page based on the initial element position (right to left).

Here is an article showing the difference with animations (which makes it easier to understand): Chaining transforms.

Here is a snippet showing the animations from the article:

html, body { height: 100%; }
body {
  background: #aaa;
  color: #000;
  font-family: Calibri,Candara,Segoe,"Segoe UI",Optima,Arial,sans-serif;
  overflow: hidden;
  margin: 0;
}
.info {
  text-align: center;
  font-family: Consolas,monaco,monospace;
  font-size: 20px;
  font-weight: bold;
  margin-bottom: 4px;
  color: #fff;
}
.split { white-space: nowrap; }
.side {
  display: inline-block;
  width: 50%;
}
.label {
  text-align: center;
  font-size: 20px;
}
.container {
  position: relative;
  font-size: 50px;
  margin: .6em auto 0;
  width: 0; height: 0;
  transform: translateX(-1em);
}
.ltr .object {
  position: absolute;
  left: 0; top: 0;
  width: 1em; height: 1em;
  margin: -.5em 0 0 -.5em;
  background: rgb(114,34,34);
  animation: ltrObj 5s infinite;
}
@keyframes ltrObj {
  from, 10% { transform: rotate( 0deg) translateX(0em); }
  40%       { transform: rotate(45deg) translateX(0em); }
  70%, to   { transform: rotate(45deg) translateX(2em); }
}
.object.shadow {
  animation: none;
  opacity: .2;
}

.ltr .axes {
  position: absolute;
  left: .5em; top: .5em;
  width: 1em; height: 1em;
  color: #111;
  box-sizing: border-box;
  border-left: 2px solid;
  border-top: 2px solid;
}
.ltr .axes::before, .ltr .axes::after {
  content: '';
  position: absolute;
  width: .2em; height: .2em;
  box-sizing: border-box;
  border-left: 2px solid;
  border-top: 2px solid;
  transform-origin: top left;
}
.ltr .axes::before { top: 100%; left: 0; margin-left: -1px; margin-top: 1px; transform: rotate(225deg); }
.ltr .axes::after { top: 0; left: 100%; margin-top: -1px; margin-left: 1px; transform: rotate(135deg); }

.rtl .axes {
  position: absolute;
  left: 0; top: 0;
  width: 2.5em; height: 2.3em;
  color: #111;
  box-sizing: border-box;
  border-left: 2px solid;
  border-top: 2px solid;
}
.rtl .axes::before, .rtl .axes::after {
  content: '';
  position: absolute;
  width: .2em; height: .2em;
  box-sizing: border-box;
  border-left: 2px solid;
  border-top: 2px solid;
  transform-origin: top left;
}
.rtl .axes::before { top: 100%; left: 0; margin-left: -1px; margin-top: 1px; transform: rotate(225deg); }
.rtl .axes::after { top: 0; left: 100%; margin-top: -1px; margin-left: 1px; transform: rotate(135deg); }

.rtl .object {
  position: absolute;
  left: 0; top: 0;
  width: 1em; height: 1em;
  margin: -.5em 0 0 -.5em;
  background: rgba(100,0,0,0.8);
  animation: rtlObj 5s infinite;
}
@keyframes rtlObj {
  from, 10% { transform: rotate( 0deg) translateX(0em); }
  40%       { transform: rotate( 0deg) translateX(2em); }
  70%, to   { transform: rotate(45deg) translateX(2em); }
}

.helper-mask {
  position: absolute;
  left: 0; top: 0;
  width: 3em; height: 3em;
  overflow: hidden;
}
.helper {
  position: absolute;
  left: 0; top: -2em;
  width: 0; height: 2em;
  margin-top: 2px;
  box-sizing: border-box;
  border: 2px solid #00c;
  border-left: none;
  border-radius: 0 100% 0 0;
  transform-origin: bottom left;
  animation: helper 5s infinite;
}
@keyframes helper {
  from, 10% { width: 0em; transform: rotate( 0deg); }
  40%       { width: 2em; transform: rotate( 0deg);}
  70%, to   { width: 2em; transform: rotate(45deg);}
}
<div class="info">rotate(45deg) translateX(2em)</div>
<div class="split">
  <div class="side ltr">
    <div class="label">Left to Right</div>
    <div class="container">
      <div class="object shadow"></div>
      <div class="object">
        <div class="axes"></div>
      </div>
    </div>
  </div>
  <div class="side rtl">
    <div class="label">Right to Left</div>
    <div class="container">
        <div class="axes"></div>
        <div class="object"></div>
        <div class="helper-mask">
            <div class="helper"></div>
        </div>
    </div>
  </div>
</div>

Whether the actual implementation uses left to right or right to left is irrelevant, both are equally valid when creating an animation, as long as you keep the difference in mind.

Bali Balo
  • 3,338
  • 1
  • 18
  • 35
7

Transforms are performed left to right. Transforms correspond to matrix operations, and these are performed left to right.

There is intuition behind it, it's not just that this is literally in the spec as a normative rule (point 3 here: https://drafts.csswg.org/css-transforms-1/#transform-rendering)

Here's a pen to try: https://codepen.io/monfera/pen/YLWGrM

Explanation:

Each transform step establishes its own coordinate system. So

transform: translateX(500px);

establishes a new coordinate system 500px along the X axis of its parent, and the element will be rendered there.

Similarly,

background-color: blue;
transform: translateX(500px) rotate(60deg);

first establishes a new coordinate system 500px along the X axis (to the right) of its parent, and only then, within that (translated, but it's now irrelevant) coordinate system does it perform the rotation. So it'll be a shape that's 500px to the right, and rotated in place (around the so-called transform-origin which is interpreted in the local coordinate system, and the default 50% 50% for rotation means, rotation around the center of the rectangle, but it's an aside).

The reverse order

background-color: orange;
transform: rotate(60deg) translateX(500px);

first establishes a new coordinate system that's rotated 60 degrees relative to the parent, and then translates 100px along the X axis of the now rotated coordinate system, in a direction that is not actually to the right from the global viewpoint of the document (or user). So, in this case, it's as if you first rotated the paper, and then slid the shape 500 units along the side of the paper (from the origin, which is in this case the top left corner).

enter image description here

For a more advanced discussion, and understanding of how it's possible to intuitively understand it for both directions, check out Composing Transformations - CSS transforms follow the post-multiplication model, so look for the page with the heading "Think of transformations as transforming the local coordinate frame" (illustrations seem to be a little off though)

Local coordinate frame - post-multiply

Robert Monfera
  • 1,980
  • 1
  • 22
  • 16
  • It seems we use different terminology for the same thing. When we speak about `f(g(x))` in math or with matrices: `A B X`, where X is a vector and A, B matrices of the transforms, the *first* thing applied to X is B, and *then* A is applied to the result. So when applying `A B` to X, B is the first transform applied – Basj Apr 25 '18 at 12:49
  • @Basj let's take the blue example. The first matrix is the translation matrix, let's call it `A`. The second is rotation, let's call it `B`. The input vector is `x` (matrices are denoted with uppercase, vectors with lowercase). Then the result is `(A B) x`, ie. first we multiply A with B, then we multiply the result with x. Left to right. You _can_ use your way if it helps, but the question was, in what order is it _done_. I tried to answer the question. Even if we forget about the spec, implementations usually combine all matrices into one, and only then multiply it with the vector. – Robert Monfera Apr 25 '18 at 16:21
  • By associativity of matrix multiplication, `(A B) x = A (B x)`, so I notice "left to right" terminology is ambiguous. When you say "left to right", you say "we first multiply A with B". When I say "right to left", I mean : "we can apply B on x first, and then apply A to the result of B x". I think we are both correct with different understanding of the expression "left to right" or "right to left" :) – Basj Apr 26 '18 at 08:53
  • 1
    Math-wise, sure. But the spec spells out the op (mmult) and its order (left to right), so it's _not_ ambiguous. Implementations need to follow _some_ order. I can think of a few possible reasons why it's spelled out, eg. limited numerical precision means that the result can differ, depending on the order. The difference can be non-negligible, so, without the rule, there would be browser differences. But possible reasons don't matter as much, because your question was, which transform is **done** first?". Spec: go left to right, in case of `A B C x`, `A` is taken first, multiplied (`A B`) etc. – Robert Monfera Apr 27 '18 at 10:57
  • Please add a runnable code snippet in the answer, so that we can test in our browser which operation is made fist. – Basj Apr 27 '18 at 12:00
  • See "Example 1 code snippet" in the accepted answer, you will see that the scaling is done first (the operation the most **on the right**), and the translation of 100px vertically after. If the 100px translation was done first, the 5x scaling would make the image at a distance 500px from the origin, which is obviously not the case. – Basj Apr 27 '18 at 12:02
  • Your example doesn't show what is _done_ first. Superficially you can do the math in either direction, but fixed precision floating point operations aren't associative. The standard is clear. To answer your question, it's sufficient. I also included a codepen with intuitive explanation. I won't have time to make examples for FP precision loss, and the burden of proof is on the person who claims it's not working in the way the standard specifies, so, until you add code to your own-accepted answer that shows, the spec is a better guide (and because I ran into FP mmult precision bugs before). – Robert Monfera Apr 28 '18 at 13:01
  • I disagree on this: floating point precision and its minor differences of order of magnitude 10^(-5) (or even it was 10^(-1) or 10^(-10)) has nothing to do with 500px offset when doing one operation before another. – Basj Apr 28 '18 at 13:20
  • `until you add code to your own-accepted answer that shows`: please run the already-existing code snippet "Example 1" of my accepted answer: you will see that the scaling is done first (the operation the most on the right), and the translation of 100px vertically after. If the 100px translation was done first, the 5x scaling would make the image at a distance 500px from the origin (0,0), which is obviously not the case :) Please run it, you will see what I mean. – Basj Apr 28 '18 at 13:22
  • You yourself said it can be interpreted in both directions. I already explained how you can intuitively interpret it left to right: via the notion of local coordinate transforms. The standard says it also, besides specifying how it's done: left to right. Maybe you're clinging to your own answer - despite admitting both directions make sense in pure math - because you think that the ops are done _to the_ shape. It's not the case. The ops are done to local coordinate spaces (L->R). Then their composition gets applied to the shape. Check the standard, if that doesn't convince you, I won't either. – Robert Monfera Apr 28 '18 at 23:28
  • As long as people can try and experiment with code snippets, they will understand (no matter the wording "right to left" or "left to right"), so I think the case is solved. – Basj Apr 28 '18 at 23:37
  • Since answering your question needs it, awareness of local coordinate systems would be key. Your Example 2 starts with `scale(5)`. The reason the thing then gets 5x farther is because this first transform isn't done _to the_ thing: it _establishes a new local coordinate system_ (direct quote from the standard). Since it's a scaling, of course each unit you move in subsequently, will be magnified 5x; you stretched the axes. That's why it'll be 5x farther away. As you say these mmults are associative so the standard's ((AB)C)x and your A(B(Cx)) equal in math, but: spec is clear; also, precision. – Robert Monfera Apr 28 '18 at 23:41
  • I suggest you include all these comments in your answer, then we can move this discusison to chat (if we keep only comments here it will make re-reading later difficult). Also please include your example in a runnable SO code snippet, it's much more handier than using a third-party website like codepen (that, by the way doesn't display well, because the rendered part is too small). – Basj Apr 29 '18 at 00:16
  • My reply gave the answer, supported it via the standard, and an explanation of linked examples, even said "...first establishes a new coordinate system 500px along the X axis (to the right) of its parent, and only then, within that (translated, but it's now irrelevant) coordinate system does it perform the rotation...", I feel like you're giving me tasks without appreciating my time and what you might have learned so far, so I'll consider your otherwise sensible (if minor) suggestions when there's more feedback. – Robert Monfera Apr 29 '18 at 00:26
  • It's not about me, we don't write answers for ourselves. It's about having an answer useful for the community. Reading an answer, jumping to codepen (rendering part very small on bottom), back to SO, reading 15 comments in discussion-style, including comments about unrelated things (floating point precision) etc. will not be easy for future readers. We all spent time on writing answers (on [that one](https://stackoverflow.com/a/45244583/1422096) it required days of work, trial and error), it's nothing personal about appreciating my time or yours. – Basj Apr 29 '18 at 00:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/170005/discussion-between-basj-and-robert-monfera). – Basj Apr 29 '18 at 00:39
  • I think what's useful to the community doesn't start with nit picks about the answer, it starts first and foremost with not misleading people with an answer that goes against the standard, and even if maybe few folks follow convos, _you_ may have already have sufficient info on what the answer is to your question. The reply even has a screenshot and people can scroll. Let's not turn it into meta SO style guidance on small details when an answer contrary to the standard is accepted, that's not how the community is served, tbh some feedback feels like excuses. It's late here, need to check out – Robert Monfera Apr 29 '18 at 00:51
  • I suggest we stop the discusison here, these comments are not a place to debate about meta things. Can we please stop the endless discussion now and move on? All the best and I wish you a good day. – Basj Apr 29 '18 at 09:27
  • The answer is spurious. https://www.stefanjudis.com/blog/order-in-css-transformation-transform-functions-vs-individual-transforms/ In the link I cited, you see the example in which the scale is written first takes more distance as compared to the second one since it uses percentage as a distance unit. Because it is applied to `scale` first, it will take more distance. – ibrahim koz Dec 19 '21 at 14:16
  • @ibrahimkoz I'm not sure what you mean by spurious. The page you cite agrees with my answer to the question: "Look at the Rendering Model sections of the CSS Transforms Module Level 1 spec and see that transform functions (translate(), rotate(), etc.) are applied from left to right" – Robert Monfera Dec 20 '21 at 09:26
1

It applies the leftmost transformation first.

As you can see in the image above, the first transformation takes a longer distance as compared to the second. The reason is the first example undergoes scale first and then it takes the distance specified by translate based on its new width on the x-axis. Because it is wider now, 50% will cause it to take a longer distance. The measure specified by 50% is calculated by taking half of the width of itself.

the site I cited from

ibrahim koz
  • 537
  • 4
  • 15
  • The conclusion after long discussions (see other answers' comments) in this topic was: it depends what we understand by "left to right" or "right to left". It's a terminology question. Both are correct, depending if we speak about the matrix multiplication (matrix associated to each transform), or "which function is applied first". MDN states *"The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied in order from right to left."*. This sums it up very well. – Basj Dec 20 '21 at 09:12
0

I just created a demo of a 3d room in HTML using CSS transforms. I made a 200x200 DIV for a back wall, leaving it in that position. Then I made a left wall starting in the same size and position, then added

transform: translate3d(-100px,0px,100px) rotateY(90deg).

Then I made a right wall and added

transform: translate3d( 100px,0px,100px) rotateY(90deg).

This created the room correctly. But this is with version 13 of Safari. Originally I tried to list the rotation step first, but the wall was in an odd position. So I'm seeing a right-to-left behavior.

Billy
  • 61
  • 1
  • 2