CSS 透视原理

Avatar of Amit Sheen
Amit Sheen

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费信用额度!

作为一名热衷于创建 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。 当原点设置在侧面时,就像您正在从该侧面“观察”对象一样。 值越大,看起来越偏向侧面。

变换

虽然 perspectiveperspective-origin 都设置在元素的父容器上,并确定消失点的 position(即从您“观察”对象的 position 到对象的平面的距离),但对象的 position 和旋转是使用 transform 属性设置的,该属性在对象本身中声明。

如果您查看上一个示例的代码,其中我将正方形从一侧移动到另一侧,您会看到我使用了 translateX() 函数——这是有道理的,因为我想让它沿着 X 轴移动。 但是请注意,它被分配给 transform 属性。 该函数是一种变换类型,它直接应用于我们要变换的元素,但其行为根据分配给父元素的透视规则。

我们可以将多个函数“链接”到 transform 属性。 但是,当使用多个变换时,有三个非常重要的事情要考虑

  1. 旋转对象时,其坐标系会与对象一起变换。
  2. 平移对象时,它会相对于其自身坐标系(而不是其父坐标)移动。
  3. 这些值书写的顺序可以(并且会)改变最终结果。

为了获得我在上一个演示中想要的效果,我首先需要在 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 立方体!

随意尝试不同的 perspectiveperspective-origin 选项,以查看它们如何影响立方体。

让我们聊聊 transform-style

我们将为我们的立方体添加一些花哨的动画,但让我们先谈谈 transform-style 属性。 我之前在通用 CSS 中添加了它,但没有真正解释它是什么或它做什么。

transform-style 属性有两个值:

  • flat(默认值)
  • preserve-3d

当我们将该属性设置为 preserve-3d 时,它会做两件重要的事情

  1. 它告诉立方体的侧面(子元素)在与立方体相同的 3D 空间中定位。 如果它没有设置为 preserve-3d,则默认值将设置为 flat,并且侧面将在立方体的平面内扁平化。 preserve-3d 将立方体视角“复制”到它的子元素(侧面),并允许我们仅旋转立方体,因此我们不需要分别为每个侧面设置动画。
  2. 它根据子元素在 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 上给我留言