HTML-坐标的含义,以及变换的使用
2017-06-30 20:26
260 查看
原文出处:http://acgtofe.com/posts/2015/12/xyz-3d-in-css
css里的3d理念
使用css3的3d transform,就可以在平面的网页里添加炫酷的三维视觉效果,这很令人愉悦。需要注意的是,3d transform只是css的一部分,它并不是一个三维引擎(3d engine)。三维引擎一般是这样的(游戏引擎Unity3D):
![](http://acgtofe.com/assets/used-images/posts/201512/unity3d_impression.png)
包括JavaScript 3D库three.js在内,简单来说,它们这些可以称为三维引擎的,都会包括:
独立的三维坐标系统。
几何图形和材质贴图。
光照和摄像机。
3d transform和它们相比起来,是缺少这些内容的。毕竟,css的关注点是网页样式,而不是创建虚拟空间。
尽管3d transform的三维空间能力有所不足,但它仍然可以创建出很棒的三维效果。这就需要我们开发者来用心了。
在下面的内容开始之前,如果你还对
perspective等3d transform相关的css属性完全不了解,可以阅读我以前写的css三维变换的文章。
3d transform的坐标系统
我们很熟悉的网页是平面的,一个DOM元素,比如一个<div>,它会有一个[b]初始坐标系(initial coordinate system):
![](http://acgtofe.com/assets/used-images/posts/201512/initial_coordinate_system.png)
每一个DOM元素都有一个这样的初始坐标系。其中,原点位于元素的左上角,z轴指向观察者(也就是屏幕外的我们)。初始坐标系的z轴并不算是三维空间,而是像
z-index那样作为参照,决定网页元素的绘制顺序,绘制顺序靠后的元素将覆盖绘制顺序靠前的。
在使用transform的时候,情况则有所不同。transform所参照的并不是初始坐标系,而是一个新的坐标系:
![](http://acgtofe.com/assets/used-images/posts/201512/transform_coordinate_system.png)
transform所用的这个坐标系,相比初始坐标系,x、y、z轴的指向都不变,只是原点位置移动到了元素的正中心。如果想要改变这个坐标系的原点位置,使用
transform-origin。
transform-origin的默认值是
50% 50%,因此,默认情况下,transform坐标系的原点位于元素中心。
transform的顺序
我们都可能像transform: rotateY(45deg) translateX(100px);这样使用多个变换函数。这种时候,需要意识到变换函数的顺序。这是因为,每一个变换函数不仅改变了元素,同时也会改变和元素关联的transform坐标系,当变换函数依次执行时,后一个变换函数总是基于前一个变换后的新transform坐标系。
例如,下面一个包含两个变换函数的transform的效果(gif):
![](http://acgtofe.com/assets/used-images/posts/201512/transform_order_1.gif)
如果交换这两个变换函数的顺序,是这样的效果:
![](http://acgtofe.com/assets/used-images/posts/201512/transform_order_2.gif)
可以看到,由于坐标系会随着每一次变换发生改变,因此不同顺序的情况下,元素最终的位置也不同。
对此还有一种解释,即变换函数是通过数学上的矩阵乘法运算完成的,而矩阵的乘法是不满足交换律的。任意坐标空间内的变换函数或者变换函数的组合,都可以转换为一个矩阵(还有一个矩阵小工具可以帮你做这个转换)。
创建三维物体
前面已经提到,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来整体移动、旋转这个正方体。
将三维物体添加到可视区域
只是有了这样的一个三维物体,我们就可以说有了一个三维场景了。但是,三维场景和平面图不同,比如这个正方体,我从不同的位置,不同的角度去观察它,我眼里看到的都是不一样的:![](http://acgtofe.com/assets/used-images/posts/201512/view_with_camera.png)
那么,这样的三维场景要如何呈现在平面的网页里呢?
这就是三维引擎里的摄像机的概念来源了。就像电影里表现同一个场景会用不同的镜头那样,我们需要定义场景里的摄像机来生成最终呈现在屏幕里的二维画面。比如three.js里的摄像机是这样的感觉:
![](http://acgtofe.com/assets/used-images/posts/201512/threejs_camera.png)
这个图里标明的是摄像机定义时使用的参数,其中包括视野范围,图像宽高比等。可以感受到还是有很多内容的。
相比之下,网页里的三维场景摄像机就弱多了,你需要用的是
perspective和
perspective-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; }
在网页里,无论你搭建了怎样的三维场景,只要你希望它显示出来,就应该像这样把构成场景的三维物体都放在一个容器元素里,然后为容器元素添加摄像机属性(
perspective和
perspective-origin)。
此外,还需要注意添加
transform-style: preserve-3d;以保证多个三维物体都位于同一空间(这样才有三维引擎的味道,对吧?)。
下面这个场景里有三个正方体,然后摄影师正在做弹跳练习(限支持3d transform的浏览器):
1
1
1
1
1
1
2
2
2
2
2
2
3
3
3
3
3
3
这段动作的动画代码是这样:
.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的两个值有一点像指定
x和
y的感觉。这里只说“有一点像”,是因为灭点位置和摄像机的位置毕竟是不同的概念,这可能还需要多看一些三维空间来体会。
那么,在上面的例子中,摄影师不只是这样跳起来,而是想要向更深处前进,应该怎么做呢?
答案是,在网页里,你不能这样移动摄像机,你需要换一个思路,参照相对运动的关系,改为让整个三维场景向你移动。不过,说到这里,前面提到的摄像机的另一个属性,
perspective,为什么它不行呢?
perspective代表摄像机距离屏幕的距离,看上去和z轴深度非常近似。但是,它并不等同于摄像机的
z坐标位置(
perspective还只能取正值),而是会影响摄像机本身的其他属性。下面用这个图说明
perspective的值变化的效果(修改自w3c的配图):
![](http://acgtofe.com/assets/used-images/posts/201512/perspective_change.png)
图中
d1和
d2分别表示两个不同的
perspective的值,其中
d2小于
d1。然后,你会惊奇地发现,一个原本位于屏幕之后(
z坐标为负值)的物体,竟然是随着“走近”而变得更小了!显然,这不符合我们在三维空间里运动的基本感受。其原因是,网页的三维投影平面是固定的,
perspective在改变摄像机的位置的同时,也同时改变了摄像机本身的其他属性(类似前面的three.js的摄像机那张图里的各种参数)。
所以,一般来说,
perspective应维持一个固定的值。想要用3d transform做出在三维空间里自由移动的效果(就像各种3d游戏),应该通过相对运动的方法实现。
其他补充
transform对布局的影响
transform影响的是视觉渲染,而不是布局。因此,除以下情况外,transform不会影响到布局:![](http://acgtofe.com/assets/used-images/posts/201512/transform_affect_layout_example.png)
这个因为
overflow生成滚动条从而影响布局的反例,也发生于
position: relative;再进行偏移的情况。
left、top等常规属性对3d transform的影响
相对于transform的translate3d()这类改变空间位置的变换函数,原来css里就有的定位属性
left、
top似乎会让情况变得很复杂。
对此,有一个比较推荐的分析方式:就三维空间的位置而言,常规属性
left、
top,甚至
margin-left等,是先生效的,它们的效果其实只有一个,就是改变元素的初始位置,从而改变元素的
transform-origin的那个原点位置,然后三维空间的transform是后生效的,它会再基于前面的
transform-origin继续改变位置。
perspective-origin和transform-origin的区别
现在你已经了解到,perspective-origin是一个摄像机的属性,定义的是透视画面的灭点,而
transform-origin是任意元素都有的,定义的是的元素的transform坐标系的原点。
结语
本文是我对目前网页里用3d transform创建三维空间的总结,其中混入了一些三维引擎的知识,并对比着来一一说明。感觉3d transform还是有自己比较明确的定位的,虽然和三维引擎相比很简陋,但它已经足够用来为网页添加吸引人的三维效果。如果你也想过用3d transform做稍复杂的三维空间,希望本文能有所帮助。
yu[/b]
css里的3d理念
使用css3的3d transform,就可以在平面的网页里添加炫酷的三维视觉效果,这很令人愉悦。需要注意的是,3d transform只是css的一部分,它并不是一个三维引擎(3d engine)。三维引擎一般是这样的(游戏引擎Unity3D):
![](http://acgtofe.com/assets/used-images/posts/201512/unity3d_impression.png)
包括JavaScript 3D库three.js在内,简单来说,它们这些可以称为三维引擎的,都会包括:
独立的三维坐标系统。
几何图形和材质贴图。
光照和摄像机。
3d transform和它们相比起来,是缺少这些内容的。毕竟,css的关注点是网页样式,而不是创建虚拟空间。
尽管3d transform的三维空间能力有所不足,但它仍然可以创建出很棒的三维效果。这就需要我们开发者来用心了。
在下面的内容开始之前,如果你还对
perspective等3d transform相关的css属性完全不了解,可以阅读我以前写的css三维变换的文章。
3d transform的坐标系统
我们很熟悉的网页是平面的,一个DOM元素,比如一个<div>,它会有一个初始坐标系(initial coordinate system):
![](http://acgtofe.com/assets/used-images/posts/201512/initial_coordinate_system.png)
每一个DOM元素都有一个这样的初始坐标系。其中,原点位于元素的左上角,z轴指向观察者(也就是屏幕外的我们)。初始坐标系的z轴并不算是三维空间,而是像
z-index那样作为参照,决定网页元素的绘制顺序,绘制顺序靠后的元素将覆盖绘制顺序靠前的。
在使用transform的时候,情况则有所不同。transform所参照的并不是初始坐标系,而是一个新的坐标系:
![](http://acgtofe.com/assets/used-images/posts/201512/transform_coordinate_system.png)
transform所用的这个坐标系,相比初始坐标系,x、y、z轴的指向都不变,只是原点位置移动到了元素的正中心。如果想要改变这个坐标系的原点位置,使用
transform-origin。
transform-origin的默认值是
50% 50%,因此,默认情况下,transform坐标系的原点位于元素中心。
transform的顺序
我们都可能像transform: rotateY(45deg) translateX(100px);这样使用多个变换函数。这种时候,需要意识到变换函数的顺序。这是因为,每一个变换函数不仅改变了元素,同时也会改变和元素关联的transform坐标系,当变换函数依次执行时,后一个变换函数总是基于前一个变换后的新transform坐标系。
例如,下面一个包含两个变换函数的transform的效果(gif):
![](http://acgtofe.com/assets/used-images/posts/201512/transform_order_1.gif)
如果交换这两个变换函数的顺序,是这样的效果:
![](http://acgtofe.com/assets/used-images/posts/201512/transform_order_2.gif)
可以看到,由于坐标系会随着每一次变换发生改变,因此不同顺序的情况下,元素最终的位置也不同。
对此还有一种解释,即变换函数是通过数学上的矩阵乘法运算完成的,而矩阵的乘法是不满足交换律的。任意坐标空间内的变换函数或者变换函数的组合,都可以转换为一个矩阵(还有一个矩阵小工具可以帮你做这个转换)。
创建三维物体
前面已经提到,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来整体移动、旋转这个正方体。
将三维物体添加到可视区域
只是有了这样的一个三维物体,我们就可以说有了一个三维场景了。但是,三维场景和平面图不同,比如这个正方体,我从不同的位置,不同的角度去观察它,我眼里看到的都是不一样的:![](http://acgtofe.com/assets/used-images/posts/201512/view_with_camera.png)
那么,这样的三维场景要如何呈现在平面的网页里呢?
这就是三维引擎里的摄像机的概念来源了。就像电影里表现同一个场景会用不同的镜头那样,我们需要定义场景里的摄像机来生成最终呈现在屏幕里的二维画面。比如three.js里的摄像机是这样的感觉:
![](http://acgtofe.com/assets/used-images/posts/201512/threejs_camera.png)
这个图里标明的是摄像机定义时使用的参数,其中包括视野范围,图像宽高比等。可以感受到还是有很多内容的。
相比之下,网页里的三维场景摄像机就弱多了,你需要用的是
perspective和
perspective-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; }
在网页里,无论你搭建了怎样的三维场景,只要你希望它显示出来,就应该像这样把构成场景的三维物体都放在一个容器元素里,然后为容器元素添加摄像机属性(
perspective和
perspective-origin)。
此外,还需要注意添加
transform-style: preserve-3d;以保证多个三维物体都位于同一空间(这样才有三维引擎的味道,对吧?)。
下面这个场景里有三个正方体,然后摄影师正在做弹跳练习(限支持3d transform的浏览器):
1
1
1
1
1
1
2
2
2
2
2
2
3
3
3
3
3
3
这段动作的动画代码是这样:
.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的两个值有一点像指定
x和
y的感觉。这里只说“有一点像”,是因为灭点位置和摄像机的位置毕竟是不同的概念,这可能还需要多看一些三维空间来体会。
那么,在上面的例子中,摄影师不只是这样跳起来,而是想要向更深处前进,应该怎么做呢?
答案是,在网页里,你不能这样移动摄像机,你需要换一个思路,参照相对运动的关系,改为让整个三维场景向你移动。不过,说到这里,前面提到的摄像机的另一个属性,
perspective,为什么它不行呢?
perspective代表摄像机距离屏幕的距离,看上去和z轴深度非常近似。但是,它并不等同于摄像机的
z坐标位置(
perspective还只能取正值),而是会影响摄像机本身的其他属性。下面用这个图说明
perspective的值变化的效果(修改自w3c的配图):
![](http://acgtofe.com/assets/used-images/posts/201512/perspective_change.png)
图中
d1和
d2分别表示两个不同的
perspective的值,其中
d2小于
d1。然后,你会惊奇地发现,一个原本位于屏幕之后(
z坐标为负值)的物体,竟然是随着“走近”而变得更小了!显然,这不符合我们在三维空间里运动的基本感受。其原因是,网页的三维投影平面是固定的,
perspective在改变摄像机的位置的同时,也同时改变了摄像机本身的其他属性(类似前面的three.js的摄像机那张图里的各种参数)。
所以,一般来说,
perspective应维持一个固定的值。想要用3d transform做出在三维空间里自由移动的效果(就像各种3d游戏),应该通过相对运动的方法实现。
其他补充
transform对布局的影响
transform影响的是视觉渲染,而不是布局。因此,除以下情况外,transform不会影响到布局:![](http://acgtofe.com/assets/used-images/posts/201512/transform_affect_layout_example.png)
这个因为
overflow生成滚动条从而影响布局的反例,也发生于
position: relative;再进行偏移的情况。
left、top等常规属性对3d transform的影响
相对于transform的translate3d()这类改变空间位置的变换函数,原来css里就有的定位属性
left、
top似乎会让情况变得很复杂。
对此,有一个比较推荐的分析方式:就三维空间的位置而言,常规属性
left、
top,甚至
margin-left等,是先生效的,它们的效果其实只有一个,就是改变元素的初始位置,从而改变元素的
transform-origin的那个原点位置,然后三维空间的transform是后生效的,它会再基于前面的
transform-origin继续改变位置。
perspective-origin和transform-origin的区别
现在你已经了解到,perspective-origin是一个摄像机的属性,定义的是透视画面的灭点,而
transform-origin是任意元素都有的,定义的是的元素的transform坐标系的原点。
结语
本文是我对目前网页里用3d transform创建三维空间的总结,其中混入了一些三维引擎的知识,并对比着来一一说明。感觉3d transform还是有自己比较明确的定位的,虽然和三维引擎相比很简陋,但它已经足够用来为网页添加吸引人的三维效果。如果你也想过用3d transform做稍复杂的三维空间,希望本文能有所帮助。
相关文章推荐
- js空间平面坐标变换(涉及文件读取,文本提取数字,特别是x,y点,以及html下拉框设计)
- linux系统中top指令的详细使用方法以及一些符号的含义
- SharpGIS博客翻译2006年7月:在SharpMap中使用即时坐标变换
- struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表
- Yii 使用CActiveForm创建表单,以及htmlOptions简单说明
- 如何使用Hpple解析HTML,以及获取网站上的所有超链接
- SharpGIS博客翻译2006年7月:在SharpMap中使用即时坐标变换
- 纯html标签下单引号和双引号以及html和JS混编下单引号和双引号,php引号的使用
- struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表
- 在Pyramid中使用Mako模板以及默认和.html后缀关联
- Windows 8技巧:Windows 8的基本变换和矩阵变换以及AppBar应用程序栏的使用
- html文件中标签的顺序以及使用ant的小TIPS
- 配置SAP的WebServices以及具體的Html使用JavaScript調用格式
- Jetty 使用 以及HTML 和Js代码无法保存问题解决
- webbench的使用以及输出结果中Speed pages/minbytes/sec的含义
- HTML中window.open的使用方法以及参数说明
- webbench的使用以及输出结果中Speed pages/minbytes/sec的含义
- (转)BUFG,IBUFG,BUFGP,IBUFGDS等含义以及使用
- js 笔记 第一章在html中使用<javascript>标签 以及 html文档模式
- 使用Javascript获取图片坐标以及宽度高度的方法