Why does transform-origin-z distort on Safari, iOS

2019-02-01 07:55发布

问题:

I've been building a prism rotation effect using 3D transforms. The transform-origin-z property seemed best for transforming the faces of the prism, but Safari 5 and Mobile Safari inexplicably stretch my element, even when no transform is applied. Firefox 12 and Chrome 18 work correctly.

Live Demo

Full Prism Demo

I'm interested in understanding why this happens. Should I avoid transform-origin-z entirely, or is there some workaround for Safari and Mobile Safari?

<style>
  /* other browser prefixes omitted for brevity */
  .container {
    margin: 50px;
    border: 2px solid #00f;
    height: 50px;
    -webkit-perspective: 500px;
  }
  .face {
    height: 50px;
    background-color: rgba(255,0,0,0.5);
    -webkit-transform-origin: center center -25px;
    -webkit-transform: translate3d(0,0,0);
  }
</style>

<div class="container">
  <div class="face"></div>
</div>​

回答1:

it seems like this is a bug in Safari. Chrome moves the transformation-center over the Z-axis, Safari leaves this center were it was, but moves the object itself over the Z-axis. The object therefore is zoomed in Safari, and seems bigger.

I would avoid the transform-origin (on Z-axis) for now and work with translate-Z to produce the same effect.

Example:

http://jsfiddle.net/willemvb/GuhcC/3/



回答2:

I believe the following explanation answers the "why" Safari is doing what it is

I do not have access to Safari for testing, but as I read the specifications for the perspective property (the same spec page you point to), it states:

The ‘perspective’ property applies the same transform as the perspective() transform function, except that it applies only to the positioned or transformed children of the element, not to the transform on the element itself.


Update on how I read the above spec

The ‘perspective’ property applies the same transform as the perspective() transform function

This tells me a perspective transform is going to be done just as if transform: perspective(500px) were applied in this case.

except that it applies only to the positioned or transformed children of the element

This tells me the perspective transform is going to be applied to child elements, in this case .face. Here, there seems to be some ambiguity. Is this saying the perspective should only be applied if another transform is done on the child element? And, does the tranform-origin property count as a transform being done to the child (especially since it is this value that relates directly to the perspective transform)? It is at this point of ambiguity that the browsers seem to differ. Safari is doing the perspective transform because the child element has tranform-origin set to -25px, whereas the others apparently are not (at least, not until the actual other transform does something else to the .face during the animation).

not to the transform on the element itself

This tells me the z=0 of .container is irrelevant, because the transform from this property is not affecting .container, but rather .container's children (i.e. .face).


So Safari appears to be taking the position that your .face always has a transform applied because you have set .container to have -webkit-perspective: 500px;, so there is always a perspective transform being applied to the child elements (.face in your case).

Note that if you take away the animation, and apply an actual transform: perspective(500px) to the .face you will see the same result in Firefox or Chrome as what you experience in Safari with your code.

So I think actually, Safari may be doing it correctly, and Firefox and Chrome perhaps are not. The spec has some ambiguity. The other two browsers perhaps should be still applying the perspective transform based off .container like Safari does, but certainly appear to not be, whereas Safari obviously appears to be.

To eliminate the issue (not have it "stick out" when "at rest"), you probably need to

  1. Animate the transform-origin itself at the beginning of your animation (and reset to 0 afterwards), OR...
  2. Animate the perspective value itself to be 0 when "at rest" and 500px when animating.

My guess is #1 will be easier to implement, but I don't know for sure.



回答3:

I don't know why this worked for me. Seem to work on all browsers. Basically I think I am canceling the css declarations effect.

.container {
   perspective: 500px;
   transform-origin: 50% 50% 25px;
}