Simulating transform-origin using translate

I want to simulate the properties of transform-origin using transform: translate in CSS.

According to MDN, this is very possible:

This property is applied by first translating the element by the negated value of the property, then applying the element's transform, then translating by the property value.

However, when I try, I get incorrect results. These two rectangles are clearly not the same:

.origin {
  transform-origin: 100px 100px;
  transform: translate(100px, 0px) scale(2) rotate(45deg);
}

.translate {
  transform: translate(-100px, -100px) translate(100px, 0px) scale(2) rotate(45deg) translate(100px, 100px);
}

.box {
  background-color: red;
  width: 100px;
  height: 100px;
}

.container {
  float: left;
  margin: 100px;
  width: 250px;
  height: 250px;
  background-color: rgba(0, 0, 0, 0.1);
}
<div class="container">
  <div class="box origin">
  </div>
</div>

<div class="container">
  <div class="box translate">
  </div>
</div>

I have tried looking for the answer without luck for quite some time now, and in my mind it should be relatively simple, I just can't figure it out.


You are almost good but you have two errors. You need to invert the translations and you need to change the transform-origin of the second one.

If you check the documentation, you will see that the reference used to translate the origin is the top left corner of the element and the default value of transform origin is center. So we need to have the same reference for both.

.origin {
  transform-origin: 50px 50px;
  transform:  rotate(45deg) scale(2);
}
.translate {
  transform-origin:0 0; 
  transform:translate(50px, 50px) rotate(45deg) scale(2) translate(-50px, -50px);
} 
.box {
  background-color: red;
  width: 50px;
  height: 50px;
}
.container {
  display: inline-block;
  margin: 30px;
  width: 150px;
  height: 150px;
  background-color: rgba(0,0,0,0.1);
}
<div class="container">
  <div class="box origin">
  </div>
</div>
<div class="container">
  <div class="box translate">
  </div>
</div>

Here is from the specification:

The transformation matrix is computed from the transform and transform-origin properties as follows:

  1. Start with the identity matrix.

  2. Translate by the computed X and Y of transform-origin

  3. Multiply by each of the transform functions in transform property from left to right

  4. Translate by the negated computed X and Y values of transform-origin

You need to pay attention to the wording! You may find that the MDN is contradictory with the specification BUT it's not the case simply because there is a difference between translating the element (like described in the MDN) and translating the origin of the element or the local coordinate (like described in the specification).

For example, translating the element by -50px is equivalent to translating its local coordinate (origin) by +50px.


You need to also pay attention to the "Multiply from left to right" as it may create confusion. If we refer to the same specification in the Example 3 we have:

div {
  height: 100px; width: 100px;
  transform: translate(80px, 80px) scale(1.5, 1.5) rotate(45deg);
}

This transformation translates the local coordinate system by 80 pixels in both the X and Y directions, then applies a 150% scale, then a 45° clockwise rotation about the Z axis. The impact on the rendering of the element can be intepreted as an application of these transforms in reverse order: the elements is rotated, then scaled, then translated.

So multiplying from left to right doesn't mean applying from left to right which somehow explain the need of inverting the translation you applied to simulate the transform-origin: