No.4 - 3D 空间的卡片翻转动效

任务目的

  • 掌握 CSS transform 3D 变形
  • 使用 CSS 伪元素触发过渡效果
  • 熟悉 CSS transform 各项子属性

任务描述

任务注意事项

  • 注意不同浏览器中的兼容性
  • 请注意代码风格的整齐、优雅
  • HTML 及 CSS 代码结构清晰、规范
  • 代码中含有必要的注释

原理分析

首先认识空间直角坐标系。(和高中略有不同)

3D变换坐标图解 张鑫旭-鑫空间-鑫生活

CSS3中的3D变换效果,本质上就是各种体位的变换。

rotateX, rotateY, rotateZ

3D transform中有下面这三个方法:

  • rotateX( angle )
  • rotateY( angle )
  • rotateZ( angle )

对应的是如下翻转:(图片来自CSS大神张鑫旭博客)

transform rotateX(45deg)的效果图 张鑫旭-鑫空间-鑫生活

transform rotateY(45deg)的效果图 张鑫旭-鑫空间-鑫生活

transform rotateZ(45deg)的效果图 张鑫旭-鑫空间-鑫生活

perspective属性:景深

perspective(透视)属性的存在与否决定了你所看到的是2次元的还是3次元的,也就是是2D transform还是3D transform. 这不难理解,没有透视,不成3D.

我们初中学美术,或者学建筑的同学肯定接触过透视的一些东西:
3D透视 张鑫旭-鑫空间-鑫生活 3D透视 张鑫旭-鑫空间-鑫生活

不过,CSS3 3D transform中的透视的透视点与上面两张示例图是不同的:CSS3 3D transform的透视点是在浏览器的前方

或者这么理解吧:显示器中3D效果元素的透视点在显示器的上方(不是后面),近似就是我们眼睛所在方位!

比方说,一个1680像素宽的显示器中有张美女图片,应用了3D transform,同时,该元素或该元素父辈元素设置的perspective大小为2000像素。则这张美女多呈现的3D效果就跟你本人在1.2个显示器宽度的地方(1680*1.2≈2000)看到的真实效果一致。

1680宽度像素显示器与3D transform视角大小示意 张鑫旭-鑫空间-鑫生活

ranslateZ帮你寻找透视位置

如果说rotateX/rotateY/rotateZ可以帮助理解三维坐标,则translateZ则可以帮你理解透视位置。

我们都知道近大远小的道理,对于没有rotateX以及rotateY的元素,translateZ的功能就是让元素在自己的眼前或近或远。比方说,我们设置元素perspective为201像素,如下:

1
perspective: 201px;

则其子元素,设置的translateZ值越小,则子元素大小越小(因为元素远去,我们眼睛看到的就会变小);translateZ值越大,该元素也会越来越大,当translateZ值非常接近201像素,但是不超过201像素的时候(如200像素),该元素的大小就会撑满整个屏幕(如果父辈元素没有类似overflow:hidden的限制的话)。因为这个时候,子元素正好移到了你的眼睛前面,所谓“一叶蔽目,不见泰山”,就是这么回事。当translateZ值再变大,超过201像素的时候,该元素看不见了——这很好理解:我们是看不见眼睛后面的东西的!

如下截图:-100时候最小,200时候超级满屏(垂直方向因特殊布局限制没有显示),250的时候因为元素已经在视点之外,因此是一片空白(看不见)。
translateZ -100像素最远距离最小显示 张鑫旭-鑫空间-鑫生活 translateZ 200像素时候超级大的显示 translateZ为250像素时候元素在视区之外,因此看不见是空白 张鑫旭-鑫空间-鑫生活

perspective属性及其两种书写

perspective属性有两种书写形式,一种用在舞台元素上(动画元素们的共同父辈元素);第二种就是用在当前动画元素上,与transform的其他属性写在一起。如下代码示例:

1
2
3
.stage {
perspective: 600px;
}

以及:

1
2
3
#stage .box {
transform: perspective(600px) rotateY(45deg);
}

结果如下缩略图:
CSS3 transform perspective两种书写形式demo效果截图

从上图我们貌似可以看到,虽然书写的形式,属性名称不一致,但是,效果貌似是一样的~~果真是这样吗???

实际上不然,上面的demo上下两个效果之所以会一样,是因为舞台上只有一个元素,因此,发生了巧合,其正好表现一样了。如果,如果舞台上有很多个元素,则两种书写形式的表现差异就会立马显示出来了!

好吧,图中的效果其实不难理解。上面舞台整个作为透视元素,因此,显然,我们看到的每个子元素的形体都是不一样的;而下面,每个元素都有一个自己的视点,因此,显然,因为rotateY的角度是一样的,因此,看上去的效果也就一模一样了!

关于Chrome浏览器以及透视盲区
前面一排门,每个门都是1米,你距离门2米,显示,当所有门都开了45°角的时候,此时,距离中间门右侧的第二个门正好与你的视线平行,这个门的门面显然就什么也看不到。这就是为什么上面右侧第三个门一片空白的元素——特定的视角以及距离形成的视觉盲区。

perspective-origin:透视中心

perspective-origin这个属性超级好理解,表示你眼睛看的位置。默认就是所看舞台或元素的中心。有时候观众感兴趣的不一定是几何中心,而有可能其他一些地方。比方说:
不同视线落地位置对应不同的perspective-origin值

一图胜千言。千言不够再来一张吧。

下面为立方体的实际应用透视效果图:

1
perspective-origin: 25% 75%;

立方体不同透视角度的效果 张鑫旭-鑫空间-鑫生活

transform-style: preserve-3d

transform-style属性也是3D效果中经常使用的,其两个参数,flat|preserve-3d. 前者flat为默认值,表示平面的;后者preserve-3d表示3D透视。

preserve-3d符合我们真实世界的思维认识。比方说,你让妹子右转了45度,此时妹子脑袋左转45度想你吐舌卖萌,妹子的脸蛋应该和你是面对面平行的。
妹子推到与transform rotateY 张鑫旭-鑫空间-鑫生活
应用transform-style: preserve-3d声明的元素确实是这样表现的,但是,如果使用默认的flat值,其效果表现——恕我想象力有限——想不通:妹子的脸还是左转45度的,同时脑袋似乎移到了身体以外的地方。

因此,基本上,我们想要根据现实经验实现一些3D效果的时候,transform-style: preserve-3d是少不了的。一般而言,该声明应用在3D变换的兄弟元素们的父元素上,也就是舞台元素。

backface-visibility:背面是否可见

在显示世界中,我们无法穿过软妹A看到其身后的软妹B或C或D;但是,在CSS3的3D世界中,默认情况下,我们是可以看到背后的元素
因此,为了切合实际,常会这样设置,使后面元素不可见:

1
backface-visibility:hidden;

任务实现

这种反转效果可以用在卡牌上。也常见于网站logo。主要是Y轴旋转的应用。

布局非常简单:

1
2
3
4
<div class="wrap"><!--舞台-->
<div class="front page"></div>
<div class="behind page"></div>
</div>

css的主要思路是

  • 在舞台设置好基本属性。
1
2
3
4
5
6
7
8
9
.wrap{
width:250px;
height: 350px;
margin:100px auto;
position: relative;
box-sizing: border-box;
transform-style: preserve-3d; /*开启3D空间*/
perspective: 1800px;
}
  • 初始状态,正面在上(初始状态未翻转),背面在下(初始状态为Y翻转180°)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.page{
width: 100%;
height: 100%;
position: absolute;
top:0;
left: 0;
background-repeat: no-repeat;
background-position: center center;
backface-visibility: hidden;/*如果不隐藏,背面是可见的*/
transition: 1s;
}

.front{
background: url('img/front.jpg');
}
.behind{
background: url('img/behind.jpg');
transform: rotateY(-180deg); /*背面的初始状态是反转的*/
}
  • 基本设置完成之后,运动一气呵成。
1
2
3
4
5
6
.wrap:hover .front{
transform: rotateY(180deg);
}
.wrap:hover .behind{
transform: rotateY(0deg);
}

参考资料