以下是 Kushagra Gour (@chinchang457) 的一篇客座文章。Kushagra 联系我向我展示了他制作的一个有趣的互动演示。它涉及 CSS 中许多 3D 变换的概念,这是我们在这里没有过多介绍的一个主题。因此,以下是 Kushagra 接手通过演示来解释这些概念。
我最近重新设计了 我的网站,并为首页和页眉想出了一个 2 面 3D 立方体的创意。悬停时,它会在我的头像和 Twitter 链接之间旋转。在这样做的过程中,我想,为什么不将这个创意扩展到一个完整的 6 面立方体,它可以用作图片画廊呢?这是我想到的!
查看 Kushagra Gour (@chinchang) 在 CodePen 上的笔 3D 立方体图片画廊
本教程概述了如何创建类似这样的东西,重点介绍 CSS3 3D 概念。
分解立方体
从立方体上可以清楚地看出,立方体的 6 个面将是 6 个不同的 HTML 元素。在这种情况下,是六个 <div>
元素。因为它们需要作为一个立方体旋转,所以它们需要在一个容器元素中。如果我们编写这个基本结构,我们将得到如下内容
<div class="cube">
<div class="cube-face"></div>
<div class="cube-face"></div>
<div class="cube-face"></div>
<div class="cube-face"></div>
<div class="cube-face"></div>
<div class="cube-face"></div>
</div>
此外,由于我们需要引用每个面来对其进行样式设置,因此我们应该向其添加相应的类。
<div class="cube">
<div class="cube-face cube-face-front"></div>
<div class="cube-face cube-face-back"></div>
<div class="cube-face cube-face-left"></div>
<div class="cube-face cube-face-right"></div>
<div class="cube-face cube-face-top"></div>
<div class="cube-face cube-face-bottom"></div>
</div>
设置面的样式
我们还没有看到任何东西。所以让我们给这些面一些尺寸和样式。
$size: 150px; // cube length
.cube {
width: $size;
height: $size;
position: relative;
}
.cube-face {
width: inherit;
height: inherit;
position: absolute;
background: red;
opacity: 0.5;
}
查看 Kushagra Gour (@chinchang) 在 CodePen 上的笔 3d 立方体画廊教程 – P1
请注意,每个立方体面都将 position
设置为 absolute
,因此它们在一个位置上彼此堆叠。现在我们可以选择每个面并相应地定位它们。
此外,我还为每个面设置了不透明度,以便我们可以透过它们看到发生了什么。
CSS3 3D 概念
让我们了解一些 CSS3 3D 的概念。为了将正面稍微靠近眼睛,我们将其沿 Z 轴平移
.cube-face-front {
color: blue;
transform: translate3d(0, 0, 20px);
}
您还没有看到任何区别。让我们了解一下原因。
透视
如 MDN 上所述
perspective CSS 属性确定 z=0 平面与用户之间的距离,以便为 3D 定位元素提供一些透视。
简单来说,它的值决定了空间中 3D 效果的程度。此属性的值越小,3D 效果就越明显。如果没有此属性,则使用 平行投影 在屏幕上呈现元素,其中投影线彼此平行。因此,无论元素向您靠近或远离多少,它看起来都将具有相同的尺寸,这与现实世界不同。(有关透视的更多信息)。
我们将为立方体的父容器设置此属性,以便其所有子元素(面)都受到共同透视的影响,如下所示
.cube {
width: $size;
height: $size;
position: relative;
perspective: 600px;
}
如预期的那样,现在正面确实看起来更大。但仍然缺少一些东西。
transform-style
即使我们为场景提供透视,我们仍然存在问题。理想情况下,正面应该出现在所有其他面的上方,将它们隐藏在其后面。但事实并非如此。
原因是我们的立方体容器没有 3D 渲染上下文,它在 CSSWG 上定义如下
一个或多个级别的包含块层次结构,由具有计算值为“preserve-3d”的“transform-style”属性的元素实例化,其元素共享一个公共的三维坐标系。
如果没有将 transform-style
属性设置为 preserve-3d
的元素,则其子元素将被呈现为扁平化,没有堆叠上下文。因此,即使我们将正面沿 Z 轴靠近,它也会继续将其渲染在其原始 z-index 上,而不会考虑其在 3D 空间中的位置。
尝试为 .cube
元素提供此属性,看看会发生什么。
.cube {
width: $size;
height: $size;
position: relative;
perspective: 600px;
transform-style: preserve-3d;
}
成功了。现在我们已经设置了 3D 系统,让我们将其转换为一个立方体!
定位面
我们将一次处理一个面,并将其放置在适当的位置。首先让我们了解 CSS 中的坐标系。如果我们要取其中一个立方体面,它将是这样的

如您所见,Z 轴从元素直接从屏幕中伸出。因此,当我们沿 Z 轴正向平移正面时,它会更靠近我们。需要注意的是,此坐标系是相对于此元素的局部坐标系。让我们更仔细地看看这一点。
我们将再次使用我们的正面并围绕其 Y 轴稍微旋转它。
.cube-face-front {
transform: rotateY(40deg);
}
现在,这是我们的正面旋转后的样子

请注意轴是如何随元素一起旋转的。这意味着 Z 轴不再直接朝向我们。相反,它位于元素的方向上。因此,如果我们现在沿 Z 轴移动它,它将沿元素面向的方向移动。
我们将使用此概念来定位立方体的每个面。假设立方体的中心位于屏幕的 2D 位置。然后需要在 3D 空间中围绕它定位立方体面。请记住,**我们旋转面使其面向所需方向,然后沿 Z 轴平移**。
正面
这里不需要旋转。只需将正面向前移动立方体长度的一半即可。
.cube-face-front {
transform: translate3d(0, 0, $size/2);
}
背面
背面将与正面相反。这意味着它需要围绕 Y 轴旋转 180 度
,然后像这样平移
.cube-face-back {
transform: rotateY(180deg) translate3d(0, 0, $size/2);
}
完成了 2 个面,还有 4 个要完成。
左面
如果您仍然对变换是如何完成的存疑,让我们通过一些视觉效果来了解这个面。
这就是左面的当前状态,平放在 2D 平面 (z=0) 上

由于左侧需要面向左侧,因此我们将其旋转 90 度

与每个面一样,我们将其沿 Z 轴移动

这是左面的最终 CSS
.cube-face-left {
transform: rotateY(-90deg) translate3d(0, 0, $size/2);
}
右面
这与左侧面相似,除了正向旋转之外。
.cube-face-right {
transform: rotateY(90deg) translate3d(0, 0, $size/2);
}
顶面
这个面需要绕 X 轴旋转 90 度
,使其朝上。
.cube-face-top {
transform: rotateX(90deg) translate3d(0, 0, $size/2);
}
底面
类似地,通过给出一个负向旋转,我们定位底面。
.cube-face-bottom {
transform: rotateX(-90deg) translate3d(0, 0, $size/2);
}
这完成了面的定位,现在我们已经完成了立方体,并具有以下最终 CSS(我还为每个面添加了随机颜色以区分它们)。
$size: 150px; // cube length
.cube {
margin: 100px;
width: $size;
height: $size;
position: relative;
perspective: 600px;
transform-style: preserve-3d;
}
.cube-face {
width: inherit;
height: inherit;
position: absolute;
background: red;
opacity: 0.8;
}
.cube-face-front {
background: yellow;
transform: translate3d(0, 0, $size/2);
}
.cube-face-back {
background: orange;
transform: rotateY(180deg) translate3d(0, 0, $size/2);
}
.cube-face-left {
background: green;
transform: rotateY(-90deg) translate3d(0, 0, $size/2);
}
.cube-face-right {
background: magenta;
transform: rotateY(90deg) translate3d(0, 0, $size/2);
}
.cube-face-top {
background: blue;
transform: rotateX(90deg) translate3d(0, 0, $size/2);
}
.cube-face-bottom {
background: red;
transform: rotateX(-90deg) translate3d(0, 0, $size/2);
}
现在要旋转立方体,我们只需在 .cube
元素上应用旋转即可。尝试围绕 Y 轴(垂直)旋转 180 度
。
.cube {
margin: 100px;
width: $size;
height: $size;
position: relative;
perspective: 600px;
transform-style: preserve-3d;
transform: rotateY(180deg);
}
你应该看到类似的东西。
查看 Kushagra Gour(@chinchang)在 CodePen 上的笔 3d cube gallery tutorial – P2。
你有没有注意到什么问题?我们将立方体围绕其垂直轴旋转了 180 度
。现在我们应该看到背面而不是正面。我们确实看到了它,但由于某种原因,它显示得更小。我们做错了什么?
修复透视
如果你还记得,我们为立方体容器(.cube
)提供了 perspective 属性。当我们刚刚旋转该元素时,消失点标记也随之旋转。因此,最初位于 2D 平面后面的消失点在旋转后来到了 2D 平面的前面,导致了这个问题。
我们理想中希望的是,无论我们转换哪个元素,透视都不会改变,保持不变。
我们如何解决这个问题?我们用另一个 DIV
包裹所有元素,并为其提供 perspective
属性。
<div class="scene">
<div class="cube">
<div class="cube-face cube-face-front"></div>
<div class="cube-face cube-face-back"></div>
<div class="cube-face cube-face-left"></div>
<div class="cube-face cube-face-right"></div>
<div class="cube-face cube-face-top"></div>
<div class="cube-face cube-face-bottom"></div>
</div>
</div>
.scene {
margin: 100px;
width: $size;
height: $size;
perspective: 600px;
}
.cube {
position: relative;
width: inherit;
height: inherit;
transform-style: preserve-3d;
transform: rotateY(180deg);
}
现在检查结果,一切应该按预期出现。
尝试给出不同的旋转,例如 transform: rotateX(30deg) rotateY(30deg)
来稍微玩一下。完成后,删除 transform
属性。
添加交互性
我们现在添加一些控件来浏览画廊。为此,我们将使用一个名为 **复选框技巧** 的巧妙技巧。虽然我们将使用单选按钮(因为一次只能选择一个)而不是复选框,但概念保持不变。你可以在 Chris Coyier 的文章中 阅读更多关于复选框技巧的内容。
不深入细节,我们向 HTML 中添加了单选按钮。
<!-- CONTROLS -->
<input type="radio" checked id="radio-front" name="select-face"/>
<input type="radio" id="radio-back" name="select-face"/>
<input type="radio" id="radio-left" name="select-face"/>
<input type="radio" id="radio-right" name="select-face"/>
<input type="radio" id="radio-top" name="select-face"/>
<input type="radio" id="radio-bottom" name="select-face"/>
<div class="scene">
<div class="cube">
<div class="cube-face cube-face-front"></div>
<div class="cube-face cube-face-back"></div>
<div class="cube-face cube-face-left"></div>
<div class="cube-face cube-face-right"></div>
<div class="cube-face cube-face-top"></div>
<div class="cube-face cube-face-bottom"></div>
</div>
</div>
以及以下 CSS 来将立方体的旋转与单选按钮绑定。
#radio-back:checked ~ .scene .cube {
transform: rotateY(180deg);
}
#radio-left:checked ~ .scene .cube {
transform: rotateY(90deg);
}
#radio-right:checked ~ .scene .cube {
transform: rotateY(-90deg);
}
#radio-top:checked ~ .scene .cube {
transform: rotateX(-90deg);
}
#radio-bottom:checked ~ .scene .cube {
transform: rotateX(90deg);
}
在上面的 CSS 中,我们简单地说明了何时选中每个单选按钮,此时立方体的旋转应该是什么。
最终代码
为了使其更美观,我们为立方体添加了一些过渡效果和适当的对齐方式,以获得以下代码。
<!-- CONTROLS -->
<input type="radio" checked id="radio-front" name="select-face"/>
<input type="radio" id="radio-left" name="select-face"/>
<input type="radio" id="radio-right" name="select-face"/>
<input type="radio" id="radio-top" name="select-face"/>
<input type="radio" id="radio-bottom" name="select-face"/>
<input type="radio" id="radio-back" name="select-face"/>
<div></div><!-- separator -->
<div class="scene">
<div class="cube">
<div class="cube-face cube-face-front"></div>
<div class="cube-face cube-face-back"></div>
<div class="cube-face cube-face-left"></div>
<div class="cube-face cube-face-right"></div>
<div class="cube-face cube-face-top"></div>
<div class="cube-face cube-face-bottom"></div>
</div>
</div>
$size: 150px; // cube length
body {
text-align: center;
padding: 50px;
}
.scene {
display: inline-block;
margin-top: 50px;
width: $size;
height: $size;
perspective: 600px;
}
.cube {
position: relative;
width: inherit;
height: inherit;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.cube-face {
width: inherit;
height: inherit;
position: absolute;
background: red;
opacity: 0.8;
}
// faces
.cube-face-front {
background: yellow;
transform: translate3d(0, 0, $size/2);
}
.cube-face-back {
background: black;
transform: rotateY(180deg) translate3d(0, 0, $size/2);
}
.cube-face-left {
background: green;
transform: rotateY(-90deg) translate3d(0, 0, $size/2);
}
.cube-face-right {
background: magenta;
transform: rotateY(90deg) translate3d(0, 0, $size/2);
}
.cube-face-top {
background: blue;
transform: rotateX(90deg) translate3d(0, 0, $size/2);
}
.cube-face-bottom {
background: red;
transform: rotateX(-90deg) translate3d(0, 0, $size/2);
}
// controls
#radio-back:checked ~ .scene .cube {
transform: rotateY(180deg);
}
#radio-left:checked ~ .scene .cube {
transform: rotateY(90deg);
}
#radio-right:checked ~ .scene .cube {
transform: rotateY(-90deg);
}
#radio-top:checked ~ .scene .cube {
transform: rotateX(-90deg);
}
#radio-bottom:checked ~ .scene .cube {
transform: rotateX(90deg);
}
向所有面添加一些 background-image
,我们得到了最终结果。
查看 Kushagra Gour (@chinchang) 在 CodePen 上的笔 3D 立方体图片画廊
希望你喜欢这次 3D 之旅,并利用它创造一些真正令人惊叹的东西!
非常好,类似于几个月前我玩过的一些东西:http://jonmann20.github.io/playground/breakdancing-cube
请发送此技巧的代码给我,谢谢!
([email protected])
很棒的立方体!
问题 1:我一直在使用你的代码,但似乎无法将所有单选按钮放在一个 DIV 中。将它们放在一个 id 为“options”的 div 中后,立方体停止工作。CSS 需要如何更改才能使其工作?
问题 2:在截取立方体的屏幕截图(使用 Windows“截图工具”)时,我发现你的立方体实际上是 288px x 288px!…???但是你的代码将其设置为 250px x 250px。这是怎么回事?
或者查看 http://desandro.github.io/3dtransforms/docs/cube.html
上周,我通过在我的一个网站上构建了一个小型纽约市,做了一些 3D 的疯狂尝试。在这里查看。
这在 Chrome 中根本不起作用。我只是看到一个随机消失的扁平图像。
我使用的是 Chrome(Mac 上的 v 28.0.1500.95),它对我很有效。
Chrome v 28.0.1500.95 m(Windows)尚未修复其与“transform-style: preserve-3d;”相关的错误,或者也许我们做错了什么¡¡¿¿??!!
我的旧 3D 立方体 在 Chrome 中不起作用,在 FF 中则一切正常。
尝试使用新的 mixin 规则来创建 -webkit-transition 和其他兼容浏览器。
很酷的技巧,而且非常流畅。我想今天是学习 SCSS 的好日子!
所以我想这在 IE10 及以下版本中将无法工作,因为它们不支持“preserve-3d”属性。有人确切知道答案吗?
https://caniuse.cn/#feat=transforms3d
太棒了。谢谢。
我之前用类似的立方体做过一个 logo:http://codepen.io/JCMais/pen/lByim
很棒的文章。我认为这可能是我见过的对此解释最直接的方式。我唯一的批评是左侧面的旋转图形让我感到困惑。它们看起来完全一样。可能只是我。我以前也经常反应慢。
除此之外,这是一篇很棒的文章!感谢你帮助我理解 CSS 3D 中的“黑魔法”。我的脑海里充满了想法。
啊,在开发工具中乱搞之后,我发现左侧面转换的图片是错误的。它们应该是 tut4.png 和 tut5.png。现在所有三个都是 tut3.png。
哦,已修复。
嘿,Chris,这篇文章有一些错误,
当你设置每个面时,SCSS 代码中都是 .cube-face-left,
我相信你明白我想说什么,
左、右、上、下。
感谢指出。我的错 :| 如果你发现任何其他错误,请告诉我。
我简直不敢相信你可以在 CSS 中做到这一切。
太棒了 - 做得好。
我可能在使用 CSS 制作这个十二面体方面有点过头了。
现在,我的问题是:如何创建具有圆角的 3D 对象?我见过使用伪元素在 CSS 中创建的 3D iPhone,但我试图弄清楚如何用两个平面创建圆角外观。是应用的渐变吗?看起来在某些角度下这种错觉会失效。我想应用动态光线会很容易地揭示出来。我可以理解创建更复杂的物体有点像要攀登一座高山。
我认为圆形表面目前还无法实现。即使在你提到的例子中,圆形表面也是使用对齐在连接手机两个面的条带角上的小平面创建的。
使用 top、left、right、top、bottom、front 和 back 类而不是 nth-child 伪类有什么好处吗?既然你使用的是 CSS 预处理器,它可以让你访问循环之类的编程函数,那么 nth-children 似乎更容易。
取决于你的使用场景。如果你想定位特定的面(比如你想在前面放最好的图片),你就必须给这些面添加类。当然,你也可以用
nth-child
来做,但类只是增加了语义。不错的教程,我现在要试试看 :)
太棒了
这很酷,但很复杂!
一如既往,一篇很棒的文章/教程,伙计们。
嘿,很棒的教程。我学到了很多东西。但是,每当我尝试这样做时,正面都无法正确显示。它看起来像是居中对齐在立方体内部,而不是像其他面一样在立方体外部。你知道我该怎么做吗?我尝试过操作 translate3d 值,但没有成功。
谢谢 Joshua!也许你可以分享一些 Codepen 上的内容,这样我就能看到哪里出错了?
当然,这是它。非常感谢你这么快就回复!
http://cdpn.io/cCHdk
导致问题的是以下注释
// faces
它在 SASS 中有效,但在 CSS 中无效(你在你的笔中使用的是 CSS)。如果你想使用 CSS,请将其更改为
/* faces */
我最喜欢的示例,我已经保存了这个链接,因为我再也找不到它从哪里链接的了。
为什么从显示右侧到显示背面时,立方体要旋转一周,而不是旋转一面?
这取决于你设置到达背面的角度。例如,如果你的角度是 90 度,要向前旋转一面,你可以设置 180 度 (+90) 或 -180 度 (-270)……后者会使立方体旋转一周。
这是一个非常棒的设计,我喜欢它,但我一直在尽我所能学习它是如何设计的,以及它是否也能在我的系统上实现,但我还没有成功,所以请告诉我该怎么做?