关于CSS3 3D Transform是变形属性里面的战斗机,就像立体几何的学习要在平面几何之后类似,在关于所有的2D的属性摸个七七八八之后,终于开始进阶3D变形了。先来一张SVG的背景图。炎炎夏日,清凉一下,即使不能行万里路,至少还有心在远方。
对于2D方向的移动,已经乏善可陈了,一个transform:translateX() 或者transform:translateY() 就能轻松实现水平和垂直方向的移动。那我们都知道,3D空间多了一个Y轴维度,4D空间多了一个时间维度,5D空间多了……就此打住,本篇不讨论科幻。(此处插播一本小说《平面国》,2D维度的世界,也比较有趣。)言归正传,显示屏展现了二维平面,那把Z轴想象成你和显示屏之间的一个距离轴线。既然如此,那transform:translateZ()是不是就能实现Z轴维度的移动了呢?你想多了。毕竟从2D到3D也是一个量变,岂能辣么简单?
1.关于容器和透视属性
先简单来说一下浏览器实现3D效果的原理,各种坐标轴什么的就不班门弄斧了,毕竟一搜一大堆。SVG向浏览器宣告“我要使用3D效果啦,做好准备”就要传递一个信号,只有translateZ是不够的,浏览器需要有一个准则,就是沿Z轴移动的比例是什么?换句话说,需要设置一个透视点perspective来配置3D空间,也就是CSS的perspective属性,再直白点说,就是我们的眼睛距离屏幕(Z轴)的距离。那perspective属性应该对谁定义呢?舞台,或者说是SVG的父容器。比如说,我定义了沿Z轴移动的动画cubic如下:
@keyframes cubic{
0% {transform: translateZ(0)}
100% {transform: translateZ(-300px)}
}
.cubic {animation:cubic 2s ease;}复制代码
作为图片而存在的SVG是没有perspective属性的,它的父容器是什么?我们都知道,SVG里面的标签,<path>
也好,<circle>
也好,都是一些绘制方法,<g>
也只是进行了组合,并非真正的父容器,这时,纯SVG有些无力了,那我们就把它放到一个<div>
里,来实现这种3D效果。既然“在我地盘这儿那就得听我的”,下面就来定义这个霸气的地盘(或者称之为舞台)透视属性。
.stage {
perspective: 200px;
background:#e5fffb; /*给舞台定义一个浅绿背景色*/
}复制代码
<body>
代码部分就简单多了,只需要<div>
里嵌套这个<SVG>
就OK了。
<div class="stage">
<svg class="cubic" xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<g>
…此处省略若干组成SVG图形的代码
</g>
</svg>
</div>复制代码
来检验一下效果如何
是不是有些失望?这明明就是缩放效果好不啦?我要你?明明一个transform:scale()就能搞定!掀桌子,*,停停停,说过Z轴是眼睛和屏幕间的轴线,沿Z轴移动可不就是缩放嘛。人家可是用二维的表现来营造观察者离屏幕移动的的场景啊(你还想怎样,难不成让图片从屏幕里出来,像贞子一样?)。
2.透视属性与变化
这里关于perspective属性值我们先定义几个极值看一下,第一个,我定义perspective:1000px,效果如下:
在同样translateZ情况下,感觉变化很小。
第二个,我定义perspective:1px,效果如下:
在同样translateZ情况下,变化灰常剧烈,缩成了一个点。
为了好理解,我们先假定地心说,宇宙万物绕着地球转。请看下图(原谅我不会3D建模,好忧桑)
左边那个场景我们想象成一个小人以45度角仰望天空,一轮明月悬空高挂,右边那个场景我们想象成一个小人头上不远的地方挂了个大南瓜吧。在视觉上,明月和南瓜是一样大小的。此时,当太阳向小人方向移动2米,小人完全看不到这种移动,但南瓜往脑袋方向移动2米,只见越来越大,我的妈呀,这是要砸下来了。所以同样的移动距离,由于视距不同,产生了差异。正所谓近大远小,也就是说perspective值越大,代表观察者离屏幕越远,那同样translateZ的移动距离,在视觉上呈现的效果越明显。
因为我把translateZ定义成了负值,就是背离眼睛方向(眼睛→屏幕),所以越来越小,那当我改一下,transform: translateZ(300px),让Z轴移动为背离屏幕情况下又是怎样的呢?
喷薄欲出,这就是南瓜砸脑袋的效果吧。
再延伸一下,当perspective为0时,会是什么效果呢,沿Z轴正向移动时,大到无极限,沿Z轴负向移动时,小到看不见?不,此极值相当于眼睛贴到了屏幕上,恭喜你,这是进入了二维世界,所以Z轴的移动对于二维平面来说,是没有变化的。这也就是为什么我们perspective不定义(默认为0)时,没有效果的原因。
3.关于透视原点perspective-origin
所有的变形动画都有一个原点(不定义,不代表没有,通常是默认值),这在tranform:rotate选择动画中表现最为明显,我们会经常定义变形原点tranform-origin来确定元素动画效果的基准点。对于我们3D动画属性来说,就要增加一个类似的概念,透视原点perspective-origin。如果说perspective值代表眼睛离屏幕的垂直距离,那perspective-origin则代表了上下左右晃动脑袋后再去观察,此动作是二维平面的动作,因此perspective-origin属性包括两个值,一个与水平X相关,一个与垂直y相关当缺省时,默认为(center center)还是上面的Z轴移动效果,来试一下改变perspective-origin属性值会发生什么。如下,我给父容器增加一个透视原点的属性值
.stage {
perspective: 200px;
background:#e5fffb;
perspective-origin: left top;
}复制代码
沿Z轴移动就变成了下面这个样子:
其他属性值就不再一一尝试了,大概也能猜个七七八八,这里除了left right top bottom center这种直接写法,还支持百分比的表示方法。感兴趣可以自行尝试。
4. 3D旋转动画
沿Z轴移动这种效果,别说众位看官没有兴趣,我也觉索然无味,单纯的移动可以靠2D的一些属性来完成,接下来试个稍微有趣点的效果,3D空间的旋转。在2D平面上的旋转transform:rotate(),是点旋转,这里的点,是指在平面上绕的点,而3D空间的旋转,则是轴旋转。绕X轴或Y轴,所以我们要把属性写成transform:rotateX()或transform:rotateY()这样子。
现在重新定义一下动画属性,先来个绕X轴旋转45度:
.cubic {animation:cubic 2s ease}
@keyframes cubic{
0% {transform: rotateX(0)}
100% {transform: rotateX(45deg)}
}复制代码
得到的动画效果:
从效果里可以明显的看出,首先,旋转方向的定义,正值是向屏幕内部旋转,如果这么说没有空间感的话,也可以理解成在Y和Z组成的平面(即与X轴垂直的平面)以笛卡尔坐标系X轴正值方向观察为顺时针旋转。算了,太别扭了,还是来张3D坐标系的图解释一下。
从动画效果里我们会发现一个奇怪的现象,就是SVG底图——变!虚!了!说好的矢量图形无限放大呢?因为不懂算法渲染什么的,个人猜测浏览器在处理这类3D效果时,先绘制了一张位图,然后以位图为蓝本进行处理,好吧,我在胡扯,具体原因等查到相关资料再更正。
改几个角度看一下,先改成-90deg,效果如下:
再来个180度翻转的:
以及360度绕轴一圈的(先打住,没意思了啊,猜都猜出效果了),这里呢,关于透视原点perspective-origin因为没做定义,所以旋转的中规中举,我随便改一下,把父容器的透视原点属性值改成perspective-origin: right bottom,也就是右下角,得到的效果是下面这样的:
绕完X轴的看过一遍之后,绕Y轴的闭着眼就能想象。随便来一个吧,现在我把绕Y轴的角度定义成120度
@keyframes cubic{
0% {transform: rotateY(0)}
100% {transform: rotateY(120deg)}
}复制代码
有没有兴趣看看绕Z轴比如transform: rotateY(120deg)的效果:
其实想想也是了,对于2D平面的图形,Z轴就是一个点,所以绕Z轴旋转就变成了普通的旋转动画效果。
还是3D的玩起来更是花样百出。兴趣指数,五颗星!
3D动画一旦玩起来,是很可怕的,前面虽然各种属性设置来过一遍,但说到底,我们用的SVG底图还是2D的,一张平面的图转来绕去的能有什么意思?总归是缺乏3D应有的使用场景。运用在多面体上的方法和效果下次再更。