本文使用「署名 4.0 国际 (CC BY 4.0)」 许可协议,欢迎转载、或重新修改使用,但需要注明来源。
作者: 百应前端团队 凌伊
常用的 3D 变换函数
CSS 位移变换 函数包括 translateX()
、translateY()
和 translateZ()
,其中 translateX()
和 translateY()
属于 2D 变换,translateZ()
属于 3D 变换。
CSS 缩放变换 函数包括scaleX()
、scaleY()
和 scaleZ()
,其中 scaleX()
和scaleY()
属于 2D 变换,scaleZ()
属于 3D 变换。
与此不同,CSS 旋转函数 中 rotateX()
、rotateY()
和 rotateZ()
均属于 3D 变换。
除此之外,
matrix3d()
也是 3D 变换的函数, 但是其参数众多(16 个参数),过于复杂并且使用场景很少,本篇文章不做介绍。
结合以上各种变换的 3D 语法,可以得到下面这些属于 3D 变换的 CSS 函数
css
translateZ(0);
translate3d(0, 0, 0);
rotateX(0deg) rotateY(0deg) rotateZ(0deg);
rotate3d(1, 1, 1, 45deg);
scaleZ(1);
scale3d(1, 1, 1);
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
rotate3d()
在展开介绍 rotate3d()
函数之前,我们有必要先了解一下 CSS 中的 3D 坐标。CSS 中的 3D 坐标中,横向为 x 轴,垂直为 y 轴,纵向为 z 轴,箭头所指的方向为偏移正值对应的方向,如下图所示:
rotate3d()
函数的语法为:rotate3d(x, y, z, angle)
其中,参数 x、y、z 分别表示旋转向量的 x 轴、y 轴、z 轴的坐标。参数 angle 表示围绕该旋转向量旋转的角度值,如果为正,则顺时针方向旋转;如果为负,则逆时针方向旋转。
如transform:rotate3d(1, 1, 1, 45deg);
表示元素绕着坐标(0, 0, 0)和坐标(1, 1, 1)连成的向量线旋转 45 度,参考下图:
rotate3d()
使用的场景其实不多,主要还是rotateX()
、rotateY()
和rotateZ()
- rotateX(angle);
- rotateY(angle);
- rotateZ(angle);
它们分别表示绕着三维坐标的 x 轴、y 轴和 z 轴旋转。其中,rotateX()
函数的表现反映在现实世界中, 体操运动员的单杠旋转;rotateY()
函数的表现反映在现实世界中,旋转木马围绕中心柱旋转;rotateZ()
函数的表现反映在现实世界中,正面观察摩天轮的旋转。
perspective
perspective 的中文意思是透视、视角。perspective 属性的存在与否决定了你所看到的画面是二维的还是三维的。
CSS 3D 变换的透视点与上图所示的有所不同,CSS 3D 变换的透视点在显示器的前方。例如, 显示器宽度是 1680px,浏览器中有一个<img />
元素设置了下面的 CSS 代码:
css
img {
perspective: 2000px;
}
这就意味着这张图片的 3D 视觉效果和本人在距离 1.2 个显示器宽度远的地方(1680×1.2≈2000)所看到的真实效果是一致的
translateZ()
我们都知道"近大远小"的道理,translateZ()
函数的功能就是控制元素在视觉上的远近距 离。例如,如果我们设置容器元素的 perspective 属性值为 201px:
css
.container {
perspective: 201px;
}
那么就会有以下几种情况:
- 子元素设置的
translateZ()
函数值越小,则子元素看起来越小,因为子元素在视觉上远去,我们眼睛看到的子元素的视觉尺寸就会变小。 - 子元素设置的
translateZ()
函数值越大,该元素看起来也会越大,因为元素在视觉上越近,看上去也就越大。 - 当子元素设置的
translateZ()
函数值非常接近 201 像素,但是不超过 201 像素的时候(如 200 像素), 该元素就会撑满整个屏幕(如果父元素没有类似 overflow:hidden 的限制)。因为这个时候,子元素相对于正好移到了你的眼睛前面,看起来非常大,可以参考🌰链接
translateZ()
函数的作用原理如图所示:
perspective 透视点
有两种书写形式可以指定元素的透视点,一种设置在舞台元素上,也就是设置在 3D 渲染元素的共同父元素上;第二种是设置在当前 3D 渲染元素上,与 transform 其他变换属性值写在一起,代码示例如下:
css
.stage .box {
transform: perspective(600px) rotateY(45deg);
}
css
.stage {
perspective: 600px;
}
.box {
transform: rotateY(45deg);
}
🌰示例效果可点击:codepen.io/pikahan-the...
上面一排元素中的每个子元素都有一个自己独立的透视点,加上旋转的角度又是一样的,因此每个元素看上去也就一模一样了。
下面一排元素把整个舞台作为透视元素,也就是我们看到的每个子元素都共用同一个透视点,因此每一个子元素的视觉形状都不一样,这个效果比较符合现实世界的 3D 透视效果。
perspective-origin
perspective-origin 属性表示我们的眼睛相对 3D 变换元素的位置,你可以通过改变眼睛的位置来改变元素的 3D 渲染效果。
语法为:perspective-origin: <position>;
css
perspective-origin: top left;
perspective-origin: right 20px bottom 40%;
perspective-origin: 50% 50%;
perspective-origin: -200% 200%;
perspective-origin: 20cm 100ch;
perspective-origin 属性初始值是 50% 50%,表示默认的透视点是舞台或元素的中心。但是有时候,需要让变换的元素不在舞台的中心,或让透视角度偏上或者偏下,此时就可以通过设置 perspective-origin 属性值实现。
transform-style
transform-style
支持两个关键字属性值,分别是 preserve-3d
和 flat
,语法如下:
css
transform-style: preserve-3d;
transform-style: flat;
-
preserve-3d 表示应用 3D 变换的元素位于三维空间中,preserve-3d 属性值的渲染表现更符合真实世界的 3D 表现。
-
flat 是默认值,表示应用 3D 变换的元素位于舞台或元素的平面中,把三维空间压缩在舞台元素的二维空间中。
通过一个例子🌰直观地了解一下 preserve-3d 和 flat 这两个属性值的区别。
应用了transform-style:preserve-3d
声明的 3D 变换元素有部分区域藏到了舞台元素的后面,因为此时整个舞台按照真实的三维空间渲染,自然看不到旋转到后面的图形区域,如图形所示。而默认情况下,元素无论怎么变换,其 3D 效果都会被渲染在舞台元素所在的二维平面之上,因此没有视觉上的穿透效果。
backface-visibility
在 CSS 中,一个元素的背面表现为其正面图像的镜像,因此,当我们使用翻转效果使其背面展示在用户面前的时候,显示的是该元素正面图像的镜像。这一特性和现实中的 3D 效果并不一致。
例如我们要实现扑克牌翻转的 3D 效果,扑克牌的背面是花纹。因此,当我们对扑克牌进行翻转使其背面展示在用户面前的时候,显示扑克牌的正面镜像显然是不合理的。我们需要隐藏扑克牌元素的背面,而扑克牌背面花纹的效果,可以使用其他元素进行模拟,然后让前后两个元素互相配合来实现 3D 扑克牌翻转效果。这个控制扑克牌的背面不显示的 CSS 属性就是backface-visibility
。
css
/* 语法 */
backface-visibility: visible; /* 默认值 */
backface-visibility: hidden;
3D 轮播图案例
这个案例使用到的 CSS 属性就是上述提到的几个常用 CSS 属性,包括透视、3D 变换和三维空间设置等。
下面跟着小编一步一步来看看这个轮播效果是怎么实现的。
首先需要实现 html 代码, 插入 9 张图片排列:
html
<div class="stage">
<div class="container">
<img src="1.jpg" style="--index:0;">
<img src="2.jpg" style="--index:1;">
<img src="3.jpg" style="--index:2;">
<img src="4.jpg" style="--index:3;">
<img src="5.jpg" style="--index:4;">
<img src="6.jpg" style="--index:5;">
<img src="7.jpg" style="--index:6;">
<img src="8.jpg" style="--index:7;">
<img src="9.jpg" style="--index:8;">
</div>
</div>
为了有 3D 透视的效果,我们需要给舞台设置视距:
css
.stage {
perspective: 800px;
}
对于容器,我们要对其进行 3D 视图的声明,不然图片只会像 2D 一样的堆叠在一起:
css
.container {
transform-style: preserve-3d;
}
之后就是对图片的处理了,首先要实现像旋转木马一样的轮播效果,很明显要用到 rotateY()
函数,又因为有 9 张图片, 360 / 90 = 40,每个图片占 40° 的旋转角。
css
img {
position: absolute;
top: 0;
left: 0;
transform: rotateY(calc(var(--index) * 40deg));
}
但现在还没有完美实现,因为图片还挤成一团, 如图:
虽然 9 张图片的方位都不一样,但由于它们共用一个 3D 变换中心点,因此一定会挤成一团,我们需要拉开图片之间的距离。
我们可以把 9 张图片想象成 9 个人,现在这 9 个人站在一起分别面朝不同方位,这 9 个人只要每个人向前走 4~5 步,彼此之间的距离就拉开了,这里的向前走 4~5 步的行为,就相当于应用translateZ()
函数的行为,当translateZ()
函数值为正值的时候,元素会向其面对的方向走去。
这个距离也是很好计算的,我们假设图片是 128px, 图片要首尾相接的话可以画出如下的图:
其中 r 的长度就是我们要的距离
javascript
function getAdjacent(x, angle) {
const radians = angle * (Math.PI / 180); // 角度转弧度
return x / Math.tan(radians);
}
getAdjacent(64,20)
算出 r 的长度约等于 175.84
为了效果更和谐,图片左右两边可以留点间距,例如 30px,通过三角函数算出 r 需要再加 20.52, 也就是最后需要 175.84 + 20.52 = 196.36 px 的距离。
于是,最终图片元素设置的 transform 属性值是:
css
img {
transform: rotateY(calc(var(--index) * 40deg)) translateZ(196.36px);
}
最后,要让图片旋转起来,只要点击让容器每次旋转 40 度就可以了。
javascript
container.onclick = function () {
const index = (this.index || 0) + 1;
this.style.transform = 'rotateY('+ index * 40 +'deg)';
this.index = index;
};
结束语
本文主要是介绍了 CSS 3D 中一些常用的属性和函数,阅读到这里,我们已然领悟到了 CSS 3D 的强大,它能创造出非常酷炫、令人惊叹的效果和动画,相信随着我们对于 CSS 3D 方面的学习摸索,定能得心应手。