问题
I have two boxes with 3d transformation (rotationY). Each have almost the same values, one perspective looks fine, the other kinda wrong but still have some correct perspective.
The first box at the top side does not protrude, but it have a perspective view yet. Also the 3° the container is 200% bigger
The second box do a beautiful 3d effect.
Here I made the example of what I'm trying to explain.
$(".eye").on('click', function () {
$( '.man' ).toggleClass('open');
})
* { padding: 0; margin: 0; }
.eye { padding: 6px 8px; }
.universe {
background: rgb(0 0 255 / 0.3);
position: absolute;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.dark {
background: rgb(0 255 0 / 0.3);
width: 25%;
height: 25%;
}
.god {
background: rgb(255 0 0 / 0.3);
transform-style: preserve-3d;
transform: perspective(800px);
}
.man {
position: absolute;
transform-origin: top left;
transition: 1s all linear;
}
.man.open {
transform: rotateY(-60deg);
}
.life {
background: rgb(255 255 0 / 0.36);
width: 25vw;
height: 25vh;
}
.no.god {
height: 100%;
}
.no.man {
position: relative;
}
.yes.god {
height: 200%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="universe">
<div class="dark">
<div class="god">
<div class="man">
<div class="life">Nothing happens until something moves.</div>
</div>
</div>
</div>
<button class="eye"> OPEN </button>
<div class="dark no">
<div class="god no">
<div class="man no">
<div class="life no">Nothing happens until something moves.</div>
</div>
</div>
</div>
<button class="eye"> OPEN </button>
<div class="dark yes">
<div class="god yes">
<div class="man yes">
<div class="life yes">Nothing happens until something moves.</div>
</div>
</div>
</div>
</div>
I think something is working wrong, but can't figure it out why. Can someone explain me why it's behaving like that?
回答1:
Each have almost the same values, one perspective looks fine,
No they don't have the same values. One is using position:absolute
and the the other one position:relative
and this make a big difference. If you inspect the god
element you will notice that its height is 0 when using the position:absolute
(the first case) which is creating the issue.
Here is a simplified code to better show your issue:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
perspective: 200px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: top left;
}
body:hover .box > div {
transform: rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
For a more accurate explanation we need to refer to the specification
Perspective can be used to add a feeling of depth to a scene by making elements higher on the Z axis (closer to the viewer) appear larger, and those further away to appear smaller. The scaling is proportional to
d/(d − Z)
whered
, the value ofperspective
, is the distance from the drawing plane to the the assumed position of the viewer’s eye.Second, the
perspective
andperspective-origin
properties can be applied to an element to influence the rendering of its 3d-transformed children, giving them a shared perspective that provides the impression of them living in the same three-dimensional scene.
Then we can see the math part:
The perspective matrix is computed as follows:
Start with the identity matrix.
Translate by the computed X and Y values of
perspective-origin
Multiply by the matrix that would be obtained from the
perspective()
transform function, where the length is provided by the value of the perspective propertyTranslate by the negated computed X and Y values of
perspective-origin
The trick is within the steps (1)(4) related to perspective-origin
. If we check the definition we can read:
The values for
perspective-origin
represent an offset of the perspective origin from the top left corner of the reference box.
Note the reference box which is the key here because this is the variable in out case (the god
element). If we add to this the fact that the default value is 50% 50%
we get our answer:
<percentage>
A percentage for the horizontal perspective offset is relative to the width of the reference box. A percentage for the vertical offset is relative to height of the reference box. The value for the horizontal and vertical offset represent an offset from the top left corner of the reference box.
Now we have all the information to understand what is happening. In the first case where the element has 0 height, the origin is on the top center (we only consider 50%
of the width) while in the second case the origin is the center since our element has a height different from 0 and more precisely a height equal to the one of the transformed element which give us a perfect result.
If we change the perspective-origin
and we consider pixel values we will have the same result for both:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
perspective: 200px;
perspective-origin:50px 30px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: top left;
}
body:hover .box > div {
transform: rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
To conclude, the default value of perspective-origin
is 50% 50%
and percentage is based on the size of the element where we apply the perspective. It's now clear that if the size is changed the origin will no more be the same which will logically give us a different rendring.
To avoid this we either set a pixel value for the origin OR we consider the use of perspective()
directly on the concerned element (the one we want to transform) and in this case we are sure the result will be the same since both elements are the same:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: center left;
transform: perspective(200px) rotateY(0);
}
body:hover .box > div {
transform: perspective(200px) rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
<div class="box" style="height:500px;">
<div style="position:absolute;">Some text here</div>
</div>
You should note in the above that perspective-origin
is irrelevant and the transform-origin
with define the origin since we are using the transform-function version of perspective.
Related questions:
perspective and translateZ moves diagonally
How to calculate angle of rotation to make width fit desired size in perspective mode?
CSS 3d transform doesn't work if perspective is set in the end of property
来源:https://stackoverflow.com/questions/63026056/why-perspective-isnt-giving-the-same-result-when-some-styles-are-updated