3d transform的坐标空间及位置

css里的3d理念

使用css3的3d transform,就能够在平面的网页里添加炫酷的三维视觉效果,这很使人愉悦。css

须要注意的是,3d transform只是css的一部分,它并非一个三维引擎(3d engine)。三维引擎通常是这样的(游戏引擎Unity3D):html

Unity3D

包括JavaScript 3D库three.js在内,简单来讲,它们这些能够称为三维引擎的,都会包括:css3

  • 独立的三维坐标系统。web

  • 几何图形和材质贴图。浏览器

  • 光照和摄像机。函数

3d transform和它们相比起来,是缺乏这些内容的。毕竟,css的关注点是网页样式,而不是建立虚拟空间。工具

尽管3d transform的三维空间能力有所不足,但它仍然能够建立出很棒的三维效果。这就须要咱们开发者来用心了。布局

在下面的内容开始以前,若是你还对perspective等3d transform相关的css属性彻底不了解,能够阅读我之前写的css三维变换的文章post

3d transform的坐标系统

咱们很熟悉的网页是平面的,一个DOM元素,好比一个<div>,它会有一个初始坐标系initial coordinate system):动画

初始坐标系

每个DOM元素都有一个这样的初始坐标系。其中,原点位于元素的左上角,z轴指向观察者(也就是屏幕外的咱们)。初始坐标系的z轴并不算是三维空间,而是像z-index那样做为参照,决定网页元素的绘制顺序,绘制顺序靠后的元素将覆盖绘制顺序靠前的。

在使用transform的时候,状况则有所不一样。transform所参照的并非初始坐标系,而是一个新的坐标系:

变换坐标系

transform所用的这个坐标系,相比初始坐标系,x、y、z轴的指向都不变,只是原点位置移动到了元素的正中心。若是想要改变这个坐标系的原点位置,使用transform-origintransform-origin的默认值是50% 50%,所以,默认状况下,transform坐标系的原点位于元素中心。

transform的顺序

咱们均可能像transform: rotateY(45deg) translateX(100px);这样使用多个变换函数。这种时候,须要意识到变换函数的顺序。这是由于,每个变换函数不只改变了元素,同时也会改变和元素关联的transform坐标系,当变换函数依次执行时,后一个变换函数老是基于前一个变换后的新transform坐标系。

例如,下面一个包含两个变换函数的transform的效果(gif):

transform顺序1

若是交换这两个变换函数的顺序,是这样的效果:

transform顺序2

能够看到,因为坐标系会随着每一次变换发生改变,所以不一样顺序的状况下,元素最终的位置也不一样。

对此还有一种解释,即变换函数是经过数学上的矩阵乘法运算完成的,而矩阵的乘法是不知足交换律的。任意坐标空间内的变换函数或者变换函数的组合,均可以转换为一个矩阵(还有一个矩阵小工具能够帮你作这个转换)。

建立三维物体

前面已经提到,3d transform并无像三维引擎那样为你建立三维场景提供全面的资源。所以,就以建立一个三维物体来讲,咱们只能利用网页目前已有的内容,本身想办法。

在网页里,你并不能直接定义一系列坐标为(x, y, z)的空间中的点,而后基于这些点来生成三维图形。网页里有的,是平面图形。不论是<div>仍是其余html元素,它们都是平的,没有厚度,像纸片同样。但纸片就能够搭东西,因此,一个DOM元素用做三维物体的一个“面”,把这些“面”有序地组织起来,获得的就是三维物体了!

事实上,在三维引擎里,三维物体也不是实体,它们都是由一系列平面(多边形)所围成的(并能够在平面上添加纹理和贴图)。

正方体

如今来作一个正方体,如今先不用考虑perspective。正方体有六个面,而后须要用一个元素来装这六个面,因此html是:

<div class="cube">
    <div class="surface surface-1">1</div>
    <div class="surface surface-2">2</div>
    <div class="surface surface-3">3</div>
    <div class="surface surface-4">4</div>
    <div class="surface surface-5">5</div>
    <div class="surface surface-6">6</div>
</div>

对应的css是(边长120px,省略浏览器私有前缀,后文同):

.cube{
    position: absolute;
    transform-style: preserve-3d;
}
.cube .surface{
    position: absolute;
    width: 120px;
    height: 120px;
    border: 1px solid #ccc;
    background: rgba(255,255,255,0.8);
    box-shadow: inset 0 0 20px rgba(0,0,0,0.2);
    line-height: 120px;
    text-align: center;
    color: #333;
    font-size: 100px;
}
.cube .surface-1 {
    transform: translateZ(60px);
}
.cube .surface-2 {
    transform: rotateY(90deg) translateZ(60px);
} 
.cube .surface-3 {
    transform: rotateX(90deg) translateZ(60px);
}
.cube .surface-4 {
    transform: rotateY(180deg) translateZ(60px);
}
.cube .surface-5 {
    transform: rotateY(-90deg) translateZ(60px);
}
.cube .surface-6 {
    transform: rotateX(-90deg) translateZ(60px);
}

其中,transform-style: preserve-3d;保证全部子元素都处于同一个三维空间(这里是三维渲染上下文3D rendering context)内,也就是告诉浏览器你是想用这些元素作一个三维场景,而不只仅只是要单个元素的简单三维效果。

position: absolute;是一个习惯作法,由于三维物体并不符合通常平面网页内容的排版,因此咱们会比较多地但愿它不要占据布局空间。

6个面位置都不同,但却都有translateZ(60px);,你已经知道这是由于巧妙搭配了在它以前的变换函数。

一旦构成正方体的6个div.surface的位置肯定后,就能够操做它们的父元素div.cube来总体移动、旋转这个正方体。

将三维物体添加到可视区域

只是有了这样的一个三维物体,咱们就能够说有了一个三维场景了。可是,三维场景和平面图不一样,好比这个正方体,我从不一样的位置,不一样的角度去观察它,我眼里看到的都是不同的:

从不一样角度观察三维物体

那么,这样的三维场景要如何呈如今平面的网页里呢?

这就是三维引擎里的摄像机的概念来源了。就像电影里表现同一个场景会用不一样的镜头那样,咱们须要定义场景里的摄像机来生成最终呈如今屏幕里的二维画面。好比three.js里的摄像机是这样的感受:

three.js的摄像机基本参数

这个图里标明的是摄像机定义时使用的参数,其中包括视野范围,图像宽高比等。能够感觉到仍是有不少内容的。

相比之下,网页里的三维场景摄像机就弱多了,你须要用的是perspectiveperspective-origin

perspective定义摄像机(也就是做为观众的咱们)到屏幕的距离,perspective-origin定义摄像机观察到的画面中的灭点(vanishing point)的位置。虽然它们并不能方便地让你直接定义摄像机的位置和观察角度等,但只要适当地应用它们,是能够必定程度上控制摄像机的画面效果的。

控制摄像机画面

网页里的摄像机通常是这样用的:

<div class="camera">
    <div class="cube1"></div>
    <div class="cube2"></div>
    <!-- more 3d objects... -->
</div>
.camera{
    position: relative;
    perspective: 1200px;
    perspective-origin: 50% 50%;
    transform-style: preserve-3d;
}

在网页里,不管你搭建了怎样的三维场景,只要你但愿它显示出来,就应该像这样把构成场景的三维物体都放在一个容器元素里,而后为容器元素添加摄像机属性(perspectiveperspective-origin)。

此外,还须要注意添加transform-style: preserve-3d;以保证多个三维物体都位于同一空间(这样才有三维引擎的味道,对吧?)。

下面这个场景里有三个正方体,而后摄影师正在作弹跳练习(限支持3d transform的浏览器):

http://runjs.cn/detail/daqoq5tf

这段动做的动画代码是这样:

.camera{
    animation: cameraMove 2s ease-out infinite alternate both;
}
@keyframes cameraMove{
    0%{
       perspective-origin: 50% 180px;
    }
    100%{
       perspective-origin: 50% -200px;
    }
}

能够看出,perspective-origin虽然是指三维透视的灭点的位置,但它的确和咱们理解的摄像机的位置是紧密关联的。若是摄像机在空间里的位置是(x, y, z)的话,perspective-origin的两个值有一点像指定xy的感受。这里只说“有一点像”,是由于灭点位置和摄像机的位置毕竟是不一样的概念,这可能还须要多看一些三维空间来体会。

那么,在上面的例子中,摄影师不仅是这样跳起来,而是想要向更深处前进,应该怎么作呢?

答案是,在网页里,你不能这样移动摄像机,你须要换一个思路,参照相对运动的关系,改成让整个三维场景向你移动。不过,说到这里,前面提到的摄像机的另外一个属性,perspective,为何它不行呢?

perspective表明摄像机距离屏幕的距离,看上去和z轴深度很是近似。可是,它并不等同于摄像机的z坐标位置(perspective还只能取正值),而是会影响摄像机自己的其余属性。下面用这个图说明perspective的值变化的效果(修改自w3c的配图):

perspective的值变化的效果

图中d1d2分别表示两个不一样的perspective的值,其中d2小于d1。而后,你会惊奇地发现,一个本来位于屏幕以后(z坐标为负值)的物体,居然是随着“走近”而变得更小了!显然,这不符合咱们在三维空间里运动的基本感觉。其缘由是,网页的三维投影平面是固定的,perspective在改变摄像机的位置的同时,也同时改变了摄像机自己的其余属性(相似前面的three.js的摄像机那张图里的各类参数)。

因此,通常来讲,perspective应维持一个固定的值。想要用3d transform作出在三维空间里自由移动的效果(就像各类3d游戏),应该经过相对运动的方法实现。

其余补充

transform对布局的影响

transform影响的是视觉渲染,而不是布局。所以,除如下状况外,transform不会影响到布局:

生成滚动条而影响布局的例子

这个由于overflow生成滚动条从而影响布局的反例,也发生于position: relative;再进行偏移的状况。

left、top等常规属性对3d transform的影响

相对于transform的translate3d()这类改变空间位置的变换函数,原来css里就有的定位属性lefttop彷佛会让状况变得很复杂。

对此,有一个比较推荐的分析方式:就三维空间的位置而言,常规属性lefttop,甚至margin-left等,是先生效的,它们的效果其实只有一个,就是改变元素的初始位置,从而改变元素的transform-origin的那个原点位置,而后三维空间的transform是后生效的,它会再基于前面的transform-origin继续改变位置。

perspective-origin和transform-origin的区别

如今你已经了解到,perspective-origin是一个摄像机的属性,定义的是透视画面的灭点,而transform-origin是任意元素都有的,定义的是的元素的transform坐标系的原点。

结语

本文是我对目前网页里用3d transform建立三维空间的总结,其中混入了一些三维引擎的知识,并对比着来一一说明。感受3d transform仍是有本身比较明确的定位的,虽然和三维引擎相比很简陋,但它已经足够用来为网页添加吸引人的三维效果。

若是你也想过用3d transform作稍复杂的三维空间,但愿本文能有所帮助。

(从新编辑自个人博客,原文地址:http://acgtofe.com/posts/2015/12/xyz-3d-in-css

相关文章
相关标签/搜索