css 3D 空间

42次阅读

共计 3356 个字符,预计需要花费 9 分钟才能阅读完成。

涉及属性

perspective
perspective-origin
transform-style
transform (rotateX, rotateY, translateZ)
transform-origin

必要条件
要实现 3D 效果必须要先理解 3D 空间是怎么形成的,以及实现 3D 效果的必要条件是什么。
Z 轴
区别与 2D 场景,3D 场景与 2D 场景最大的区别是有了 Z 轴,那么 Z 轴在 css 中该如何产生呢?
这就涉及到上面列出的第一个属性: perspective(景深) MDN。
首先我们必须要知道 Z 轴如果生成了会有什么效果,最直观的感受就是元素在 Z 轴 上面平移会有大小变化。
我们都说空间是由一个个平面构成,也就是说如果说我们规定一个与元素平行的平面,以这个平面为基础去定义元素平面的 Z 轴 那么 3D 空间就产生了。这也是 perspective 的作用所在,因此 perspective 属性可以认为是开启 3D 场景。

perspective 规定了观察者距离垂直于元素 2D 平面下的距离。
简单的例子就是一张纸我们可以理解为一个简单的元素,纸面就是 2D 空间的平面,如果设定了 perspective: 100px; 就相当于你拿起了这张纸(纸面与你的面平行),那么这张纸和你这个人就够成了 3D 空间。
总结来说,3D 空间的构成必须要有一个参照平面,两个不同的平面才能照成视差,也就产生了 3D 的效果,而 perspective 就规定了这两个参照平面的距离。
需要注意的是:
当一个元素有了 perspective 属性后,元素的大小并不会发生变化。所以当一个元素的 css 为
perspective: 100px;
width:100px;
height:100px;
可以理解为,在距离观察平面 100px 处有一个看上去是 100X100 的元素,注意是看上去,这和拍照是同一个道理,设置的大小就是照片拍出来后在照片中的大小。
perspective 仅仅支持正值,负值或是 0 不生效。
faq: 针对于网上一些文章中发现的问题

设置了 perspective 属性就相当于这距离元素多远处放置一只眼睛然后观察元素这样的描述不太对,一只眼睛相当于一个点,而不是一个平面,这不仅仅确定了 Z 轴同时还确定了 X 与 Y 轴,但是产生的效果确实和这样的描述相符合。原因是因为,默认的 perspective-origin 变换点为元素的中心,这点不用设置 perspective 就可以确定,所以如果设置了 perspective-origin 这样的描述就不对了。所以可以这么认为 眼睛的位置由 perspective-origin 确定,而这只眼睛距离元素多远由 perspective 属性确定。

设置 perspective 属性就相当于产生了垂直于屏幕平面的 Z 轴。这个描述也不对,仅仅特定的情况下生效,那就是元素的父元素没有 3D 变换,或是有 3D 变换但没有旋转 X 或 Y 轴。原因如下:一个元素在没有 3D 空间的情况下是永远和屏幕在同一个平面的,所以垂直于屏幕等效于垂直于元素,但是如果说元素的父元素处在 3D 空间内,并且进行了 X 轴 或 Y 轴 的旋转,那么这个观察平面是不会和屏幕平行,那么变换所依据的 Z 轴 也不会垂直于电脑平面。

transform 变换
有了 3D 空间,如果不进行变换的话是没有任何效果的,所以产生 3D 效果的第二个条件就是变换。
变换基于 3D 坐标轴,所以使用 transform 变换最重要的是确定 3D 坐标轴。
那么如何确定 3D 坐标轴?
很简单,我们找出坐标原点即可,就是 transform-origin MDN 的值,默认值为 transform-origin: 50% 50% 0; 也就是元素的正中心。
需要注意的是如果没有 3D 空间,transform-origin 的第三个值是无效的,因为对于元素来说,它并不知道观察点距离自己多远,也就进行不了视差的变化。
faq: 必须要理解的几个问题

transform 到底在改变什么?transform 改变的是坐标轴,而不是元素,元素的呈现是随便坐标轴的变化而变化的,比如 transform: scale(.5); 就是坐标轴缩小了,那么对应元素的显示就缩小了,并不是元素的大小缩小了,而坐标轴没变,元素的真实大小永远不会变,变的是元素在不同坐标轴下的呈现。当然除非修改 transform-origin,坐标轴对于元素来说永远不会变。理解了这个那么对于 transform 的应用应该也没有问题。

不同的元素是否在同一个坐标轴下?不是。每个元素都有自己坐标轴,即使是父子元素也是处在不同的坐标轴下,但是子元素会受父元素影响,子元素的起始坐标轴和父元素的最终坐标轴一致。举个例子,当父元素的 X 轴旋转了 45deg,那么子元素坐标轴的起始位置就已经旋转了 45deg。

3D 效果的叠加
在上一节的 faq 中的第二点,如果实验的话,应该是有点小问题的,因为在浏览器中,一个元素的内容默认是以 2D 效果呈现的,也就是 transform-style: flat;。关于这个属性我们来具体的实验一下,结果通过实验来获得。
实现的前提条件是:有一个 3 层嵌套的 div,在最外层的 div 上设置 perspective 生成 3D 空间,最深一级的 div 上添加旋转,代码大致如下
<div class=”box1″>
<div class=”box2″>
<div class=”box3″></div>
</div>
</div>
.box1 {
perspective: 300px;
}
.box3 {
transform: rotateY(45deg);
}
接着我们来控制 box2
1 . box2 无变换,不添加 transform-style: preserve-3d
点击查看:jsfiddle。可以发现 box3 处在 box2 的 3D 空间中,box2 的背景成了空间的背景。
2 . box2 无变换,添加了 transform-style: preserve-3d
点击查看:jsfiddle。可以发现 box2 成了 3D 变换中的一员,由于 box3 的旋转,导致有一部分旋转到了 box2 的后面,结果就是看不见了。换句话说 box2 在 box1 的 3D 空间中,box3 也在 box1 的 3D 空间。
3 . box2 有变换,不添加 transform-style: preserve-3d 点击查看:jsfiddle。可以发现 box2 处在 3D 空间中,而 box3 处在 box2 的平面里。
4 . box2 有变换,添加了 transform-style: preserve-3d
点击查看:jsfiddle。结果和情况 2 呈现的效果一致,两个元素都处在 box1 的 3D 空间里。
分析:

前面我们说过:perspective 会开启一个 3D 空间,确定了其子元素的 Z 轴,因此 box2 是能做 3D 变换的。所以 3 4 情况中 box2 的状态我们可以确定,就是这样呈现的。

3 4 情况中的区别,在于 box3 的呈现,而代码上的区别在于 box2 有没有添加 transform-style: preserve-3d 这条属性,那么这条属性的效果就是将子元素放在 3D 空间中显示,这条属性的另一个值 flat 也就是默认值就是将子元素压平在父元素中显示,就好像往父元素中塞了一张照片。
对比 2 4 发现,在添加了 transform-style: preserve-3d 的情况下,父元素的变换并不会影响到子元素。但对比 1 2 发现,不添加 transform-style: preserve-3d 时,事情有点出乎意料。box2 成了变换的 3D 空间,但是 box2 上却没有 perspective 属性。
对于第 3 点,我们是不是可以认为当子元素没有进行变换的时候,3D 空间是会被赋给子元素??这点不太确定,表现如此,但也找不到相关的内容,有待查证。

总结

perspective 规定了一个与元素平行的平面,生成了 3D 空间,与 perspective-origin 配合确定了视觉的观察点。

transform-origin 规定了一个元素的 3D 坐标原点,前提元素处在 3D 空间内。

transform 可以将元素的坐标轴进行变换,不直接修改元素,元素只是对于坐标轴的呈现。

transform-style preserve-3d: 将子元素以 3D 效果呈现。flat: 将子元素沿着父元素的 Z 轴压扁,效果和拍照一致。
当祖父元素开启了 3D 空间,而父元素没有进行变换,3D 空间会延续到父元素上。
最后一点有待考证。

正文完
 0