JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://unpkg.com/rematrix"></script>
<div style="transform: perspective(1000px) rotateY(30deg) rotateX(-30deg);display:inline-block;width:300px;">
<div style="transform:translateZ(30px)">
<div class="flatten" style="transform:translateZ(30px)">
<div style="transform:translateZ(30px)">
<div style="transform:translateZ(30px)">
<div style="transform:translateZ(30px);">preserve-3d
</div>
</div>
</div>
</div>
</div>
</div>
<div style="transform: perspective(1000px) rotateY(30deg) rotateX(-30deg);display:inline-block;width:300px;vertical-align:top;margin-left:30px;">
<div style="transform:translateZ(30px)">
<div class="portal">
<div class="mover">
<div style="transform:translateZ(30px)">
<div style="transform:translateZ(30px)">
<div style="transform:translateZ(30px);">flat
</div>
</div>
</div>
</div>
</div>
</div>
</div>
CSS
div:not(.mover){
transform-style: preserve-3d;
border: 2px solid red;
padding: 8px;
background: #eee;
}
.flatten{
border: 2px solid blue !important;
}
.portal{
/* `overflow: hidden` should be used here instead of `transform-style: flat !important`, but I have it removed for testing */
transform-style: flat !important;
border: 2px solid blue !important;
transform: translateZ(30px);
}
.mover{
transform-style: preserve-3d;
}
JavaScript
// I use https://github.com/jlmakes/rematrix for basic matrix math, however this library doesn't support complex operations like decomposition.
Object.assign(window, Rematrix)
// Utility to generate matrix based on transforms
function transform(...step) {
return step.reduce(multiply, identity())
}
function normalize(matrix){
return Array.from(matrix, (v) => v / matrix[15])
}
// Calculate matrix of top-level div
const parentMatrix = transform(
rotateY(30),
rotateX(-30),
perspective(1000),
)
// Calculate matrix of the portal element
// We can ignore padding/border offset since that is alreadt applied before transform
const portalTransform = transform(
parentMatrix,
translateZ(30),
translateZ(30),
)
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// normalize portal transform
const normalizedPortalTransform = normalize(portalTransform)
// try to re-establish perspective in z axis
const final = identity()
final[8] = normalizedPortalTransform[8]
final[9] = normalizedPortalTransform[9]
final[10] = normalizedPortalTransform[10]
final[11] = normalizedPortalTransform[11]
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
document.querySelector('.mover').style.transform = toString(final)