原文地址
http://css-tricks.com/creating-a-3d-cube-image-gallery/
先看一下最终完成的效果,以冲淡一下译文的乏味与枯燥:点击Demo进行查看效果。
拆解立方体
一个立方体很明显是由六个面组成的,而这六个面将分别是六个HTML元素组成的,在这个案例中,这六个HTML元素分别是六个div
。因为它们将要被旋转变形成一个立方体,所以它们需要被一个容器包含起来,如果我们按照这个思路的话,可以写出这样的HTML:
<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; }
得到如下效果:
注意,现在每个面已经设置了绝对定位,所以它们叠在了一起,一会儿我们可以单独对每个面相对于外层的div
进行定位。
另外 ,我还给了每个面设置成了半透明,这样一会儿方便观察。
CSS3中的3D
让我们先来了解一下CSS3中3D的概念,现在我们把立方体中最近面的面拉近一点,则需要让它在Z轴进行变化:
.cube-face-front { color: blue; transform: translate3d(0, 0, 20px); }
添加了上面的CSS样式后,我们将看不到任何变化,why?
透视(perspective)
关于perspective,在MDN是这样定义的:
The perspective CSS property determines the distance between the z=0 plane and the user in order to give to the 3D-positioned element some perspective.
CSS中的perspective属性决定了z=0时的平面到用户给元素设置了3D定位后的距离。(这个地方我翻译不好,所以把原话贴在了上面,脑子里想的很清楚,就是翻译不出来…这种感觉真TM难受!)。
简单来说,这个值决定了元素在空间里的3D效果的强弱程度,这个值设置的越小,就会得到越强的3D效果,如果不设置这个值的话,那么元素在则会以“平等投影的方式Parallel projection”渲染出来,所以上面的例子中,不管你怎么设置元素的Z轴上的值,它都没有变化。
于是 ,我们可以在立方体外层容器上设置perspective
属性,这样,内部的所有六个面都会受到透视影响:
.cube { width: $size; height: $size; position: relative; perspective: 600px; }
按照预期,现在前面的面(.cube-face-front
)应该变大了,但是还少了一些东西。
transform-style
我们已经给当前场景设置了perspective值,但是现在还有一个问题。按道理来说,前面的面应该出现在其它面是上面,但是现在却没有,为什么呢?
这是因为,立方体容器没有设置为3D渲染环境(3D rendering context)。
在没有设置 transform-style
的属性为 preserve-3d
的情况下,它的子元素则会以平面化的形式渲染出来。所以我们上面对Z轴的变化是无效的。
现在,给外层容器(.cube
)添加 transform-style: preserve-3d;
,看看有什么变化:
.cube { width: $size; height: $size; position: relative; perspective: 600px; transform-style: preserve-3d; }
已经看到效果了,现在整体环境已经搭建好了,开始把它变化成一个立方体了!
定位各个面
我们需要将各个面定位到合适的地方,首先,我们需要知道CSS中的坐标系统是什么样的,如果我们是正对着立方体的前面一个面,那么坐标如图所示:
可以看到,Z轴是与屏幕垂直的,因此,我们变幻了元素在Z轴上的值后,它离我们更近了。这里需要注意的是:这个坐标系是想对于元素而言的,让我们来深究一下。
还是设置立方体前面的一个面,现在让它在Y轴旋转40度:
.cube-face-front { transform: rotateY(40deg); }
现在坐标系则也跟着旋转,如图所示:
在浏览器中的效果:
有了上面这些理论做为基础后,我们将要设置立方体的每个面。假设立方休的中心点是在屏幕的某个位置上,那么立方体的每个面将围绕这个点从而形成立方体。
前面
前面不需要旋转角度,只是在Z轴上进行移动而已(移动的距离为立方体尺寸的一半):
.cube-face-front { transform: translate3d(0, 0, $size/2); }
背面
背面由应该与前面相反,所以,我们需要将背面在Y轴旋转180度,让它翻个身,原来再设置Z轴的位置:
.cube-face-back { transform: rotateY(180deg) translate3d(0, 0, $size/2); }
左面
左面则需要在Y轴旋转90度,想象一下,它旋转后正对我们则类似于一张纸片,Z轴则一样设置:
.cube-face-left { transform: rotateY(-90deg) translate3d(0, 0, $size/2); }
右面
右面则与左侧想反:
.cube-face-right { transform: rotateY(90deg) translate3d(0, 0, $size/2); }
顶部
顶部则需要在X轴旋转90度,然后想对于Z轴做移动即可:
.cube-face-top { transform: rotateX(90deg) translate3d(0, 0, $size/2); }
底部
底部与顶部相反:
.cube-face-bottom { transform: rotateX(-90deg) translate3d(0, 0, $size/2); }
现在各个面都各就各位了,现在我们把整个立方体旋转180度(Y轴)的话,则可以看到盒子的背面:
.cube { margin: 100px; width: $size; height: $size; position: relative; perspective: 600px; transform-style: preserve-3d; transform: rotateY(180deg); }
为了方便观察 ,我给各个面加了不同的背景色,现在可以看到这样的效果:
你可以点击Demo进行查看效果。
等等。。。这个效果不对吧?转了180度,怎么会是这个样子的呢?应该只显示背面才对啊?难道是我们做错了什么?(没错,是的~~)
修正perspective
不知你是否还记得,我们之前给外层的容器(.cube
)设置了perspective
属性,随后我们对它进行了旋转,而消失点也会随之旋转,所以旋转过来后,我们发现的是“近小远大”,很明显,这是不符合常理的。
我们需要的是视角不随着元素的旋转而改变 ,也就是消失点不随之改变。
那么如何解决这个问题呢?
我们可以在盒子的外面再套一个div
,然后再给这个div设置persepctive
属性,把里面的.cube
的perspective
属性删掉:
.scene { margin: 100px; width: $size; height: $size; perspective: 600px; } .cube { position: relative; width: inherit; height: inherit; transform-style: preserve-3d; transform: rotateY(180deg); }
这个时候的效果是符合我们预期的:
你可以点击Demo查看效果。
最后 ,给立方体旋转不同的角度试试:
.cube { width: $size; height: $size; position: relative; @include transition(transform,0.2s); @include transform-style(preserve-3d); @include transform(rotateX(30deg) rotateY(30deg)); &:hover{ @include transform(rotateX(-30deg) rotateY(-30deg)); } }
点击Demo进行查看效果。