5

I found transform property depends on perspective() position

why is this happening? any other rules/limitations for transform?

though it feels strange to me, this not seems to be a bug as I am able to reproduce this in Chrome/FF

box:nth-child(1):hover {
  transform: perspective(1000px) translate3d(0, 0, -100px);
}

box:nth-child(2):hover {
  transform: translate3d(0, 0, 100px) perspective(1000px);
}

box {
  padding: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: monospace;
  transition: transform .4s;
  background: rgba(255, 0, 0, 0.3);
  margin: 20px;
  font-size: 12px;
  perspective: 1000px;
  cursor: pointer;
}

box:nth-child(2) {
  background: rgba(0, 0, 255, 0.3);
}
<box>
  transform: perspective(1000px) translate3d(0,0,100px);
</box>
<box>
  transform: translate3d(0,0,100px) perspective(1000px);
</box>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
godblessstrawberry
  • 4,556
  • 2
  • 40
  • 58
  • 2
    Browser has no z-dimension information normally (why it need?). Once without telling the browser the z-position of element, how can you do 3-dimensional translation? – MT-FreeHK Aug 17 '18 at 08:38
  • 1
    See: [Intro to CSS 3D transforms: Perspective](https://3dtransforms.desandro.com/perspective) – insertusernamehere Aug 17 '18 at 08:41
  • @MatrixTai read about transform3d and you wil understand – Temani Afif Aug 17 '18 at 08:41
  • @TemaniAfif , what I mean is OP must declare the perspective first, it initializes the 3d-space. This is just a inverse question. – MT-FreeHK Aug 17 '18 at 08:47
  • @MatrixTai why the order really matters? I am a bit confused because when I write e.g. `transform: translate3d(0,0,100px) rotate3d(1,0,0,45deg)` order doesnt matter, but for perspective it does – godblessstrawberry Aug 17 '18 at 08:47
  • 1
    @godblessstrawberry it does matter in that case also ... invert the order and you want have the same result, you have the same result only in some particular case – Temani Afif Aug 17 '18 at 08:50
  • @TemaniAfif wow that's really surprising, I just came from 2d transforms world where (I'm not sure anymore) the order wasn't matter - my bad I expected same from 3d – godblessstrawberry Aug 17 '18 at 08:59
  • @godblessstrawberry even for the 2d it matters a lot ;) you will find in my answer a link a to a previous question dealing with 2d transfrom and you will see – Temani Afif Aug 17 '18 at 09:01
  • @godblessstrawberry, it does matter for order of `transform: translate3d(0,0,100px) rotate3d(1,0,0,45deg)`. I will make an answer cover for this too. But in theory, Temani's answer is pretty enough. – MT-FreeHK Aug 17 '18 at 09:09

2 Answers2

4

You should make the perspective first in both cases. If you add it at the end the translation will be first made without considering the perspective.

If we refer to the specification we can see how the transformation matrix is computed:

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

As you can see in the step (3), it's from left to right (here is another question where you can get more information and see why order is important: Simulating transform-origin using translate)

It also useless to use the perspective property within the element you want to transform.

box:nth-child(1):hover {
  transform: perspective(1000px) translate3d(0, 0, -100px);
}

box:nth-child(2):hover {
  transform: perspective(1000px) translate3d(0, 0, 100px);
}

box {
  padding: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: monospace;
  transition: transform .4s;
  background: rgba(255, 0, 0, 0.3);
  margin: 20px;
  /*perspective: 1000px;*/
  font-size: 12px;
  cursor: pointer;
}

box:nth-child(2) {
  background: rgba(0, 0, 255, 0.3);
}
<box>
  transform: perspective(1000px) translate3d(0,0,100px);
</box>
<box>
  transform:  perspective(1000px) translate3d(0,0,100px);
</box>

To avoid any confusion with order you can declare the persepective within a parent element BUT you need to pay attention to the origin as it won't be the same:

box:nth-child(1):hover {
  transform:translate3d(0, 0, -100px);
}

box:nth-child(2):hover {
  transform:translate3d(0, 0, 100px);
}
body {
  perspective:1000px;
}
box {
  padding: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: monospace;
  transition: transform .4s;
  background: rgba(255, 0, 0, 0.3);
  margin: 20px;
  font-size: 12px;
  cursor: pointer;
}

box:nth-child(2) {
  background: rgba(0, 0, 255, 0.3);
}
<box>
  transform: perspective(1000px) translate3d(0,0,100px);
</box>
<box>
  transform:  perspective(1000px) translate3d(0,0,100px);
</box>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Something I don't get though. Why declaring perspective to the body as it was already declared for box element? – PIIANTOM Aug 17 '18 at 08:51
  • 1
    @PIIANTOM I moved the declaration from the box to the body – Temani Afif Aug 17 '18 at 08:52
  • 1
    @PIIANTOM I got your point, if you talk about the perspective declaration inside the box and not inside the transform [that I didn't notice] it useless in both cases, I removed it ... declaration perspective on an element affect only the transformed child not the element – Temani Afif Aug 17 '18 at 08:58
1

Though another answer already give quite clear statement how perspective() works. But I want to make I bit more concrete.

box:nth-child(1):hover {
  transform: perspective(1000px) translate3d(0, 0, 100px);
}

box:nth-child(2):hover {
  transform: translate3d(0, 0, 100px) perspective(1000px);
}

box:nth-child(3):hover {
  transform: rotate3d(1, 0, 0, 45deg) perspective(1000px) translate3d(0, 0, 100px);
}

box:nth-child(4):hover {
  transform: rotate3d(1, 0, 0, 45deg) translate3d(0, 0, 100px) perspective(1000px);
}

box:nth-child(5):hover {
  transform: rotate3d(1, 0, 0, 45deg) translate3d(0, 0, 100px);
}

box:nth-child(6):hover {
  transform: translate3d(0, 0, 100px) rotate3d(1, 0, 0, 45deg);
}

box:nth-child(7):hover {
  transform: perspective(1000px) rotate3d(1, 0, 0, 45deg) translate3d(0, 0, 100px);
}

box:nth-child(8):hover {
  transform: perspective(1000px) translate3d(0, 0, 100px) rotate3d(1, 0, 0, 45deg);
}

box {
  padding: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: monospace;
  transition: transform .4s;
  background: rgba(255, 0, 0, 0.3);
  margin: 20px;
  font-size: 12px;
  perspective: 1000px;
  cursor: pointer;
}

box:nth-child(even) {
  background: rgba(0, 0, 255, 0.3);
}
<box>
  1. transform: perspective(1000px) translate3d(0,0,100px);
</box>
<box>
  2. transform: translate3d(0,0,100px) perspective(1000px);
</box>
<box>
  3. transform: rotate3d(1,0,0,45deg) perspective(1000px) translate3d(0, 0, 100px);
</box>
<box>
  4. transform: rotate3d(1,0,0,45deg) translate3d(0, 0, 100px) perspective(1000px);
</box>
<box>
  5. transform: rotate3d(1,0,0,45deg) translate3d(0, 0, 100px);
</box>
<box>
  6. transform: translate3d(0, 0, 100px) rotate3d(1,0,0,45deg);
</box>
<box>
  7. perspective(1000px) rotate3d(1,0,0,45deg) translate3d(0, 0, 100px);
</box>
<box>
  8. perspective(1000px) translate3d(0, 0, 100px) rotate3d(1,0,0,45deg);
</box>

First of all, for example 1 and 2. It is quite obvious showing how perspective() work for translate3d.

But is that mean without perspective(), translate3d is useless?

No. As I mentioned in the very first command.

without telling the browser the z-position of element, how can you do 3-dimensional translation?


But, how about 2-dimensional?

Take a look on example 3 and 5. They behave completely different.

Why? Because after you do the rotation, its z-dimension is no longer your windows' z-dimension. The block move upward as 100 * cos(45) = 50px.

And therefore, 5 and 6 work completely different, the order between rotate3d(1,0,0,45deg) and translate3d(0, 0, 100px) does make difference.

For 7 and 8 it is much more obvious when also the z-index becomes available for the element. It does difference.

godblessstrawberry
  • 4,556
  • 2
  • 40
  • 58
MT-FreeHK
  • 2,462
  • 1
  • 13
  • 29