首页 > 解决方案 > 3d 盒子的网格(只有两个面)

问题描述

我有一个只有两个面(正面和底部)的 3d 盒子网格。每个盒子都有自己的视角。悬停时,盒子旋转;底面朝前。例如:

.grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 1000px;
    margin: 50px auto;
}
.box-wrapper {
    width: 25%;
    height: 250px;
    perspective: 1000px;
}
.box {
    width: 100%;
    height: 100%;
    position: relative;
    transition: transform .5s;
    transform-style: preserve-3d;
}
.box .face {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    border: 1px solid black;
    display: flex;
    justify-content: center;
    align-items: center;
}
.box .face.front {
    transform: translateZ(123.5px);
    background-color: hsla(0, 100%, 50%, .5);
}
.box .face.bottom {
    transform: rotateX(-90deg) translateZ(123.5px);
    background-color: hsla(120, 100%, 50%, .5);
}
.box:hover {
    transform: rotateX(90deg);
}
<div class="grid">
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
</div>

现在,这个设置的问题是盒子之间的重叠。我可以手动消除重叠,为此我需要进行以下更改/计算:

而且由于一项更改会影响其他更改,因此这是有问题的。

例如,要消除上述设置(1000px 容器)中的重叠,需要进行以下更改:

.box-wrapper {
    width: calc(25% - 29px); // why 29?
    margin-right: 29px;
    margin-bottom: 29px;
    height: 221px;
}
.box .face.front {
    transform: translateZ(110.5px); // half of box's width
}
.box .face.bottom {
    transform: rotateX(-90deg) translateZ(110.5px);
}

.grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 1000px;
    margin: 50px auto;
}
.box-wrapper {
    width: calc(25% - 29px);
    margin-right: 29px;
    margin-bottom: 29px;
    height: 221px;
    perspective: 1000px;
}
.box {
    width: 100%;
    height: 100%;
    position: relative;
    transition: transform .5s;
    transform-style: preserve-3d;
}
.box .face {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    border: 1px solid black;
    display: flex;
    justify-content: center;
    align-items: center;
}
.box .face.front {
    transform: translateZ(110.5px);
    background-color: hsla(0, 100%, 50%, .5);
}
.box .face.bottom {
    transform: rotateX(-90deg) translateZ(110.5px);
    background-color: hsla(120, 100%, 50%, .5);
}
.box:hover {
    transform: rotateX(90deg);
}
<div class="grid">
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
</div>

我也打算让这个移动友好,所以随着窗口宽度的变化,我会改变容器的宽度(.grid),并进行上述更改。所以我唯一的变量是容器宽度,它会随着窗口宽度的变化而变化,一个盒子应该占据容器宽度的 25%。如何自动进行此计算?由于计算包括透视和变换,我还不能想出任何东西。

即使我手动(试错)计算大屏幕断点的值(宽度、边距、变换),在某些时候我也需要使用 JS 自动计算,例如低于 768 像素。

标签: javascriptcsscss-animationscss-transforms

解决方案


很明显,这个问题来自于translateZ结合的角度。您正在通过积极的翻译使您的元素更接近您,而视角正在创造重叠。

为了避免这种情况,我们可以避免translateZ使用其他配置来创建类似的效果。这是一个想法:

.grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 1000px;
    margin: 50px auto;
}
.box-wrapper {
    width: 25%;
    height: 250px;
    perspective: 1000px;
}
.box {
    width: 100%;
    height: 100%;
    position: relative;
    transition: transform .5s, transform-origin 0.5s;
    transform-style: preserve-3d;
}
.box .face {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    border: 1px solid black;
    display: flex;
    justify-content: center;
    align-items: center;
    box-sizing:border-box;
}
.box .face.front {
    background-color: hsla(0, 100%, 50%, .5);
}
.box .face.bottom {
    transform: rotateX(-90deg) translateY(50%) translateZ(125px);
    background-color: hsla(120, 100%, 50%, .5);
}
.box:hover {
    transform: rotateX(90deg) translateY(-100%);
    transform-origin:top;
}
<div class="grid">
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
</div>

正如你所看到的,translateZ前面的元素没有更多了,所以最初没有更多的重叠,然后我调整transform-origin和平移以补偿translateZ应用于底部元素。

所有使用的值都是使用百分比的相对值,因此您在计算时不会有任何问题。我还添加box-sizing:border-box了在高度/宽度计算中包含边框,以便您可以250px / 2 = 125pxtranslateZ.

更新

我添加了一个过渡transform-origin,以便以某种方式从中心旋转,而不是完全从顶部旋转。

更新 2

正如评论中所建议的,我们也可以更改transform-orgincenter center -125px

.grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 1000px;
    margin: 50px auto;
}
.box-wrapper {
    width: 25%;
    height: 250px;
    perspective: 1000px;
}
.box {
    width: 100%;
    height: 100%;
    position: relative;
    transition: transform .5s;
    transform-style: preserve-3d;
    transform-origin:center center -125px;
}
.box .face {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    border: 1px solid black;
    display: flex;
    justify-content: center;
    align-items: center;
    box-sizing:border-box;
}
.box .face.front {
    background-color: hsla(0, 100%, 50%, .5);
}
.box .face.bottom {
    transform: rotateX(-90deg) translateY(50%) translateZ(125px);
    background-color: hsla(120, 100%, 50%, .5);
}
.box:hover {
    transform: rotateX(90deg);
}
<div class="grid">
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
    <div class="box-wrapper">
        <div class="box">
            <div class="face front">front face</div>
            <div class="face bottom">bottom-face</div>
        </div>
    </div>
</div>


推荐阅读