欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

无立体,不动画,CSS3 3D 动画属性入门

程序员文章站 2022-03-25 16:42:14
...

关于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应有的使用场景。运用在多面体上的方法和效果下次再更。