作为一名热衷于创建 CSS 动画的人,我使用的一个更强大的工具是 perspective
。 虽然 perspective
属性本身无法实现 3D 效果(因为基本形状无法具有深度),但您可以使用 transform
属性在 3D 空间(使用 X、Y 和 Z 轴)中移动和旋转对象,然后使用 perspective
控制深度。
在本文中,我将尝试解释 perspective
的概念,从最基础的内容开始,逐步构建一个完整的 3D 立方体动画。
透视的基本原理
我们从一个简单的绿色正方形开始,并将其在所有三个轴上移动。
虽然在 X 和 Y 轴上移动对象非常简单,但如果我们在 Z 轴上移动它,它看起来像正方形保持完全相同,这是因为当对象在 Z 轴上移动时,动画会将其移近我们,然后远离我们,但正方形的大小(和位置)保持不变。 这就是 CSS perspective
属性发挥作用的地方。
虽然 perspective
在对象在 X 或 Y 轴上移动时没有影响,但当对象在 Z 轴上移动时,perspective
会使正方形看起来更大,当它移近我们时,更小,当它远离我们时。 是的,就像在“现实”生活中一样。
当我们旋转对象时,会发生同样的效果
在 Z 轴上旋转正方形看起来像我们都熟悉和喜欢的普通旋转,但当我们在 X 或 Y 轴上旋转正方形(不使用透视)时,它看起来只是正方形变小(或变窄)而不是旋转。 但是当我们添加 perspective
时,我们可以看到当正方形旋转时,正方形的近侧看起来更大,远侧看起来更小,旋转看起来如预期。
请注意,当对象在 X 或 Y 轴上的旋转达到 90°(或 270°、450°、630° 等)时,它将“消失”在视野之外。 同样,这是因为我们无法为对象添加深度,并且在这个位置,正方形的宽度(或高度)实际上将为 0。
透视值
我们需要使用一个值设置 perspective
属性。 此值设置了从对象的平面到消失点的距离,或者换句话说,透视强度。 值越大,您距离对象越远; 值越小,透视效果越明显。
透视原点
perspective-origin
属性决定了您“观察”对象的的位置。 如果原点居中(这是默认设置)并且对象向右移动,它看起来像您正在从左侧(反之亦然)观察它。
或者,您可以让对象保持居中,并移动 perspective-origin
。 当原点设置在侧面时,就像您正在从该侧面“观察”对象一样。 值越大,看起来越偏向侧面。
变换
虽然 perspective
和 perspective-origin
都设置在元素的父容器上,并确定消失点的 position(即从您“观察”对象的 position 到对象的平面的距离),但对象的 position 和旋转是使用 transform
属性设置的,该属性在对象本身中声明。
如果您查看上一个示例的代码,其中我将正方形从一侧移动到另一侧,您会看到我使用了 translateX()
函数——这是有道理的,因为我想让它沿着 X 轴移动。 但是请注意,它被分配给 transform 属性。 该函数是一种变换类型,它直接应用于我们要变换的元素,但其行为根据分配给父元素的透视规则。
我们可以将多个函数“链接”到 transform
属性。 但是,当使用多个变换时,有三个非常重要的事情要考虑
- 旋转对象时,其坐标系会与对象一起变换。
- 平移对象时,它会相对于其自身坐标系(而不是其父坐标)移动。
- 这些值书写的顺序可以(并且会)改变最终结果。
为了获得我在上一个演示中想要的效果,我首先需要在 X 轴上平移正方形。 只有这样我才能旋转它。 如果以相反的方式进行(先旋转,然后平移),那么结果将完全不同。
为了强调值顺序对于 transform
属性的重要性,让我们看几个简单的例子。 首先,两个正方形的简单二维(2D)变换,它们都具有相同的变换值,但声明顺序不同
即使我们在 Y 轴上旋转正方形,也是一样的
需要注意的是,虽然值的顺序很重要,但我们只需更改值本身即可获得所需的结果,而不是更改值的顺序。 例如…
transform: translateX(100px) rotateY(90deg);
…将具有与…相同的效果
transform: rotateY(90deg) translate<strong>Z(100px);
这是因为在第一行中,我们在旋转它之前在**X 轴**上移动了对象,但在第二行中,我们旋转了对象,改变了它的坐标,然后在**Z 轴**上移动了它。 相同的结果,不同的值。
让我们看看更有趣的东西
当然,正方形是解释透视一般概念的好方法,但当我们分解成三维(3D)形状时,我们开始真正看到透视是如何工作的。
让我们使用迄今为止涵盖的所有内容来构建一个 3D 立方体。
HTML
我们将创建一个 .container 元素,它围绕一个 .cube 元素,而 .cube 元素又包含六个代表立方体面的元素。
<div class="container">
<div class="cube">
<div class="side front"></div>
<div class="side back"></div>
<div class="side left"></div>
<div class="side right"></div>
<div class="side top"></div>
<div class="side bottom"></div>
</div>
</div>
通用 CSS
首先,我们将为父 .container
元素添加一些透视。 然后,我们将确保 .cube
元素具有 200px 的边,并尊重 3D 变换。 我在这里添加了一些演示样式,但关键属性已突出显示。
/* The parent container, with perspective */
.container {
width: 400px;
height: 400px;
border: 2px solid white;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
perspective: 800px;
perspective-origin: top right;
}
/* The child element, with 3D tranforms preserved */
.cube {
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
}
/* The sides of the cube, absolutely positioned */
.side {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.9;
border: 2px solid white;
}
/* Background colors for the cube's sides to help visualize the work */
.front { background-color: #d50000; }
.back { background-color: #aa00ff; }
.left { background-color: #304ffe; }
.right { background-color: #0091ea; }
.top { background-color: #00bfa5; }
.bottom { background-color: #64dd17; }
变换侧面
正面是最简单的。 我们将其向前移动 100px
.front {
background-color: #d50000;
transform: translateZ(100px);
}
我们可以通过添加 translateZ(-100px)
将立方体的背面向后移动。 另一种方法是将侧面旋转 180deg,然后将其向前移动
.back {
background-color: #aa00ff;
transform: translateZ(-100px);
/* or */
/* transform: rotateY(180deg) translateZ(100px); */
}
与背面一样,我们可以用几种方法变换左右侧面
.left {
background-color: #304ffe;
transform: rotateY(90deg) translateZ(100px);
/* or */
/* transform: translateX(100px) rotateY(90deg); */
}
.right {
background-color: #0091ea;
transform: rotateY(-90deg) translateZ(100px);
/* or */
/* transform: translateX(-100px) rotateY(90deg); */
}
顶部和底部略有不同。 我们不需要在 Y 轴上旋转它们,而是在 X 轴上旋转它们。 同样,它可以通过多种不同的方式完成
.top {
background-color: #00Bfa5;
transform: rotateX(90deg) translateZ(100px);
/* or */
/* transform: translateY(-100px) rotateX(90deg); */
}
.bottom {
background-color: #64dd17;
transform: rotateX(-90deg) translateZ(100px);
/* or */
/* transform: translateY(100px) rotateX(90deg); */
}
这将给我们一个 3D 立方体!
随意尝试不同的 perspective
和 perspective-origin
选项,以查看它们如何影响立方体。
transform-style
让我们聊聊 我们将为我们的立方体添加一些花哨的动画,但让我们先谈谈 transform-style
属性。 我之前在通用 CSS 中添加了它,但没有真正解释它是什么或它做什么。
transform-style
属性有两个值:
flat
(默认值)preserve-3d
当我们将该属性设置为 preserve-3d
时,它会做两件重要的事情
- 它告诉立方体的侧面(子元素)在与立方体相同的 3D 空间中定位。 如果它没有设置为
preserve-3d
,则默认值将设置为flat
,并且侧面将在立方体的平面内扁平化。preserve-3d
将立方体视角“复制”到它的子元素(侧面),并允许我们仅旋转立方体,因此我们不需要分别为每个侧面设置动画。 - 它根据子元素在 3D 空间中的位置显示子元素,而与它们在 DOM 中的位置无关。
此示例中有三个正方形——绿色、红色和蓝色。 绿色正方形的 translateZ
值为 100px,这意味着它在其他正方形的前面。 蓝色正方形的 translateZ
为 -100px,这意味着它在其他正方形的后面。
但在 DOM 中,正方形的顺序是:绿色、红色、蓝色。 因此,当 transform-style
设置为 flat(或根本没有设置)时,蓝色正方形将显示在顶部,而绿色正方形将在后面,因为这是 DOM 的顺序。 但如果我们将 transform-style
设置为 preserve-3d
,它将根据其在 3D 空间中的位置进行渲染。 结果,绿色正方形将在前面,而蓝色正方形将在后面。
动画
现在,让我们为立方体设置动画! 为了让事情更有趣,我们将把动画添加到所有三个轴。 首先,我们将 animation
属性添加到 .cube
。 现在它还不会有任何作用,因为我们还没有定义动画关键帧,但它已经到位,以便我们稍后使用它。
animation: cubeRotate 10s linear infinite;
现在是关键帧。 我们基本上将沿每个轴旋转立方体,以便它看起来像是在太空中滚动。
@keyframes cubeRotate {
from { transform: rotateY(0deg) rotateX(720deg) rotateZ(0deg); }
to { transform: rotateY(360deg) rotateX(0deg) rotateZ(360deg); }
}
perspective
属性实际上是赋予动画深度的关键,就像我们看到立方体左右滚动,以及前后滚动。
但是在此之前,perspective
属性的值一直保持不变,perspective-origin
也是如此。 让我们看看更改这些值如何影响立方体的外观。
我在此示例中添加了三个滑块,以帮助查看不同的值如何影响立方体的视角
- 左侧滑块设置
perspective
属性的值。 请记住,此值设置了从物体平面的距离,因此值越小,视角效果就越明显。 - 另外两个滑块对应于
perspective-origin
属性。 右侧滑块设置垂直轴上的原点,从上到下,而底部滑块设置水平轴上的原点,从右到左。
请注意,在动画运行时,这些更改可能不太明显,因为立方体本身在旋转,但您可以通过单击“运行动画”按钮轻松关闭动画。
使用这些值进行试验,找出它们如何影响立方体的外观。 没有一个“正确”的值,这些值因项目而异,因为它们取决于动画、物体的大小以及您想要实现的效果。
接下来呢?
既然您已经掌握了 CSS 中 perspective
属性的基础知识,您可以充分发挥您的想象力和创造力,在您自己的项目中创建 3D 对象,为您的按钮、菜单、输入以及您想要“赋予生命”的任何其他内容添加深度和趣味性。
同时,您可以尝试创建一些 复杂结构 和基于视角的动画,例如 这个、这个、这个,甚至 这个,来练习和提高您的技能。

我希望您喜欢阅读这篇文章,并且在这个过程中学到了新东西! 随时留下评论,告诉我您的想法,或者如果您对视角或这篇文章中的任何其他主题有任何疑问,请 在 Twitter 上给我留言。
哇! 谢谢。 我一直回避视角,直到现在。
感谢您提供这篇文章,我一直试图为我的动画和徽标添加 3D 效果。 直到今天,我才了解如何使用“translateZ”属性,也不知道拥有“perspective”(CSS 生活中的视角)的力量。
这太棒了。 非常棒的写作,真的有助于深入理解,这样我们就可以将其应用到我们的项目中。 谢谢!
感谢 Amit,
这让我对视角有了更清晰的认识。 我从某篇文章中了解了视角,并没有真正理解,但实际上我一直正确地使用它。 让我重新审视我的桌面。 我脑海中涌现出许多项目。 有些我可能无法实现。 LOlz!
很棒的工作! 我喜欢它!
如果它能正确地处理组合变换的应用顺序,那就太好了。 根据 MDN(重点突出)
因此,
rotate(30deg) translateX(140px)
示例可以通过首先沿着 x 轴平移框,然后将其旋转 30deg 来最好地可视化。 每个组合变换都有一个等效的变换矩阵。 它们从左到右相乘。感谢您的努力,但许多示例都非常卡顿,以至于我无法真正理解正在解释的内容(Firefox 80/Mac)。
太棒了! 学习 CSS 透视的绝佳方法。
Mark,我会尝试一下。
CSS 动画中最重要的事情之一。
这真的丰富了我的知识。 我非常感谢。
很棒的文章,我学到了很多。
您在
transform
属性的规则中做出了有趣的选择。 规则 1 指出“当旋转物体时,它的坐标系会随着物体一起变换”。 您将此与变换函数从左到右应用的想法结合起来。MDN 上的半官方文档 MDN 讲述了一个不同的故事:变换函数从右到左应用,而 X-Y-Z 轴不会随着物体旋转或移动。
从数学角度来看,这两种方法是等效的。 我会说保持轴不变的优点在于您不需要在脑海中旋转轴。 例如,如果第一个变换函数是
rotateZ(90deg)
,您需要记住 X 轴现在指向下方,而 Y 已变为水平方向。 从右到左读取不会对您的思维造成这种要求。经过更多思考后,要补充这些评论:当变换函数是
scale
时,“变换轴”图像会变得特别复杂。 然后程序员被迫想象对同一单位有不同长度的轴。例如,想象一个 100px * 100px 的元素。 在进行任何变换之前,它的左边缘位于 X=-50px,它的右边缘位于 X=50px。 现在我们将其变换
在 MDN 的从右到左的图像中,它首先向右移动 50px,因此它的边缘位于 X=0 和 X=100px; 然后 X 坐标加倍,因此边缘位于 X=0 和 X=200px。 这个结果就是我们实际看到的。
根据您的图片,坐标首先被翻倍,变为 X=-100px 和 X=100px,然后… 对象向右移动 50px _现在宽度为 100px,因为 X 轴的比例也翻倍了。_ 所以在 Y 和 Z 轴上 50px 就是 50px,而在 X 轴上 50px 的距离实际上是 100px。
结果依然正确,但对程序员的想象力要求很高。
这篇文章很棒!谢谢您!