CSS 3D 变化的那些事

本文使用「署名 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;
}

那么就会有以下几种情况:

  1. 子元素设置的 translateZ()函数值越小,则子元素看起来越小,因为子元素在视觉上远去,我们眼睛看到的子元素的视觉尺寸就会变小。
  2. 子元素设置的 translateZ()函数值越大,该元素看起来也会越大,因为元素在视觉上越近,看上去也就越大。
  3. 当子元素设置的 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-3dflat,语法如下:

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 方面的学习摸索,定能得心应手。

相关推荐
爱上语文10 小时前
HTML和CSS 表单、表格练习
前端·css·html
小肚肚肚肚肚哦11 小时前
盘点浏览器盒模型中各种 width、height 、边距和位置属性
css·html
NightCyberpunk11 小时前
HTML、CSS
前端·css·html
南城FE11 小时前
12个更现代化的CSS单行优化技巧
前端·css
啵咿傲14 小时前
重绘&重排、CSS树&DOM树&渲染树、动画加速 ✅
前端·css
前端Hardy14 小时前
HTML&CSS:数据卡片可以这样设计
前端·javascript·css·3d·html
流烟默14 小时前
CSS中Flex布局应用实践总结
前端·css·flex布局
大G哥16 小时前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
前端·css
Au_ust20 小时前
css:感觉稍微高级一点的布局
前端·css
下页、再停留20 小时前
【前端】CSS修改div滚动条样式
前端·css