原文地址

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;
}

得到如下效果:

1

注意,现在每个面已经设置了绝对定位,所以它们叠在了一起,一会儿我们可以单独对每个面相对于外层的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;
}

2

已经看到效果了,现在整体环境已经搭建好了,开始把它变化成一个立方体了!

定位各个面

我们需要将各个面定位到合适的地方,首先,我们需要知道CSS中的坐标系统是什么样的,如果我们是正对着立方体的前面一个面,那么坐标如图所示:

tut1

可以看到,Z轴是与屏幕垂直的,因此,我们变幻了元素在Z轴上的值后,它离我们更近了。这里需要注意的是:这个坐标系是想对于元素而言的,让我们来深究一下。

还是设置立方体前面的一个面,现在让它在Y轴旋转40度:

.cube-face-front {
  transform: rotateY(40deg);
}

现在坐标系则也跟着旋转,如图所示:

tut2

在浏览器中的效果:

3

有了上面这些理论做为基础后,我们将要设置立方体的每个面。假设立方休的中心点是在屏幕的某个位置上,那么立方体的每个面将围绕这个点从而形成立方体。

前面

前面不需要旋转角度,只是在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);
}

为了方便观察 ,我给各个面加了不同的背景色,现在可以看到这样的效果:

4

你可以点击Demo进行查看效果。

等等。。。这个效果不对吧?转了180度,怎么会是这个样子的呢?应该只显示背面才对啊?难道是我们做错了什么?(没错,是的~~)

修正perspective

不知你是否还记得,我们之前给外层的容器(.cube)设置了perspective属性,随后我们对它进行了旋转,而消失点也会随之旋转,所以旋转过来后,我们发现的是“近小远大”,很明显,这是不符合常理的。

我们需要的是视角不随着元素的旋转而改变 ,也就是消失点不随之改变。

那么如何解决这个问题呢?

我们可以在盒子的外面再套一个div,然后再给这个div设置persepctive属性,把里面的.cubeperspective属性删掉:

.scene {
  margin: 100px;
  width: $size;
  height: $size;
  
  perspective: 600px;
}
.cube {
  position: relative;
  width: inherit;
  height: inherit;
  
  transform-style: preserve-3d;
  transform: rotateY(180deg);
}

这个时候的效果是符合我们预期的:

5

你可以点击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));
	}
}

6

点击Demo进行查看效果。