高效视差

Paul Lewis
Robert Flack
Robert Flack

无论爱是爱还是讨厌,视差技术都会持续存在。谨慎使用时, 来增加 Web 应用的深度和微妙之处。不过,问题在于 因此可能具有挑战性。在本文中,我们将介绍 讨论一种既高效又同样重要的解决方案 。

视差图示。

要点

  • 请勿使用滚动事件或 background-position 来创建视差动画。
  • 使用 CSS 3D 转换来创建更准确的视差效果。
  • 对于 Mobile Safari,请使用 position: sticky 来确保视差效果 进行传播

如果您需要普适性解决方案,请前往 UI 元素示例 GitHub 代码库,并获取 视差辅助 JS! 您可以在以下位置观看视差滚动条的实时演示: GitHub 代码库。

问题视差器

首先,我们来看看实现视差的两种常见方法 特别是为什么它们不适合我们的用途。

坏:使用滚动事件

视差的关键要求是它应该滚动耦合;用于 每当页面滚动位置发生变化时,视差元素的 。这听起来很简单, 现代浏览器异步工作的能力。这一点适用于我们的 和滚动事件在大多数浏览器中,滚动事件 “尽力而为”并且不一定会在 滚动动画效果!

这条重要信息告诉我们,为什么需要避免 基于 JavaScript 的解决方案,根据滚动事件移动元素: JavaScript 并不保证视差技术能跟上 滚动位置。在旧版本的 Mobile Safari 中,滚动事件 实际上是在滚动结束时才投放的 基于 JavaScript 的滚动效果。较新的版本确实会传递滚动事件 但与 Chrome 浏览器类似,基础。如果 主线程正忙于任何其他工作,滚动事件将不会被传送 这意味着视差效果将会丢失。

错误:正在更新“background-position

我们要避免的另一种情况是在每一帧上绘制。多种解决方案 尝试更改 background-position 以提供视差外观, 会使浏览器在滚动时重新绘制网页中受影响的部分 代价可能足以使动画出现明显的卡顿。

如果我们想要实现视差运动的承诺, 可作为加速属性(目前意味着坚持使用 变形和不透明度),并且不依赖于滚动事件。

3D CSS

Scott KellumKeith Clark 在使用 CSS 3D 实现视差运动方面做了大量工作, 他们实际使用的技术就是:

  • 设置一个包含元素以使用 overflow-y: scroll(可能 overflow-x: hidden)。
  • 向该元素应用 perspective 值和 perspective-origin 设置为 top left0 0
  • 向该元素的子元素应用 Z 轴平移,然后将其缩小 以提供视差运动,而不会影响其在屏幕上的大小。

此方法的 CSS 如下所示:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

这里假设 HTML 代码段如下所示:

<div class="container">
    <div class="parallax-child"></div>
</div>

调整透视缩放比例

将子元素往回推,会使其与 视角值。您可以使用 此公式:(透视 - 距离)/透视。由于我们最有可能 希望视差元素呈现视差效果,但以我们编写的尺寸显示, 则需要以此方式进行扩展,而不是保持原样。

对于上述代码,透视是 1pxparallax-child 的 Z 距离为 -2px。也就是说,该元素 要放大 3 倍,您可以看到插入代码中的值: scale(3)

对于任何未应用 translateZ 值的内容,您可以 替换成零的值。这意味着该比例为 (perspective - 0) / 透视图,其值为 1,表示经 既不向上也不向下真的非常方便。

此方法的工作原理

一定要弄清楚这种验证方式的有效性,因为我们将使用 知识。滚动实际上是一种转换 accelerated;主要涉及到使用 GPU 来回移动层。在 典型的滚动(没有任何角度概念的滚动) 在比较滚动元素及其子元素时,会采用 1:1 的方式。 如果将某个元素向下滚动 300px,其子元素将向上转换 相等金额:300px

但是,对滚动元素应用透视值会造成混乱 此流程;它会更改支持滚动转换的矩阵。 现在,300px 的滚动可能只会将子视图移动 150px,具体取决于 您选择的 perspectivetranslateZ 值。如果某个元素的 translateZ 值为 0,则它会以 1:1 的比例滚动(和以往一样),但子元素 按 Z 远离透视原点的位置,以不同的角度 评分!最终结果:视差运动。而且,请务必注意 浏览器内部滚动机制的一部分,这意味着 无需监听 scroll 事件或更改 background-position

迷幻药剂:Mobile Safari

每种效果都有一定的说明性,而对于转换,其中一个重要的是 子元素保留了 3D 效果。如果 具有透视效果的元素及其视差子元素之间的层次结构, 3D 透视是“扁平化”的,表示效果已丢失。

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

在上面的 HTML 中,.parallax-container 是新的,它会有效地 并展平了 perspective 值,这样就会失去视差效果。解决方法: 在大多数情况下,这相当简单:只需添加 transform-style: preserve-3d 使它能够传播任何 3D 效果(比如我们的透视效果) 值)。

.parallax-container {
  transform-style: preserve-3d;
}

而在 Mobile Safari 中,则要稍微复杂一些。 从技术上讲,将 overflow-y: scroll 应用于容器元素是可行的, 快速滑动滚动元素的成本解决方法是将 -webkit-overflow-scrolling: touch,但它也会展平 perspective 这样就无法实现任何视差效果

从渐进式增强的角度来看,这可能并不太 问题。如果我们无法在每种情况下都能实现视差,我们的应用仍会正常运行,但 最好能找到一个解决方法。

position: sticky前去救援!

事实上,系统会以 position: sticky 的形式提供一些帮助, 允许元素“粘滞”附加到视口顶部或指定父元素 。与大多数规范一样,该规范相当庞大,但其中包含 以下实用小宝石:

乍一看,这可能并不太重要, 当它指的是某个元素的黏度 计算:"偏移是通过参考最近的祖先实体计算得出的 一个滚动框”。也就是说,粘性元素的移动距离 (要让它显示为附加到其他元素或视口)为 在应用任何其他转换之前(而不是之后)计算。这意味着 与前面的滚动示例非常相似,如果偏移 在 300 像素处,这为您提供了使用视角(或任何其他转换)的新机会 将 300 像素的偏移值应用到任何粘性广告之前 元素。

通过对视差元素应用 position: -webkit-sticky,我们可以: 实际上是“逆向”-webkit-overflow-scrolling: touch 的扁平化效果。这样可确保视差元素引用 祖先实体具有一个滚动框,在本例中为 .container。然后, 与之前类似,.parallax-container 会应用 perspective 值, 这会更改计算出的滚动偏移,并产生视差效果。

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

这样就能恢复移动 Safari 的视差效果, 一轮!

粘性定位注意事项

这里有区别,但是:position: sticky 确实会改变 视差机制。粘性定位会尝试将元素固定在 滚动容器,非粘性版本则不然。这意味着 但具有粘性的视差最终是与无以下特征的视差相反的:

  • 如果使用 position: sticky,元素越接近 z=0,它就越小 移动。
  • 如果没有 position: sticky,元素越接近 z=0, 它就会移动

如果这一切看起来有点抽象,您可以观看这个演示,由 Robert Flack 完成, 演示了使用和不使用粘性元素时元素的行为方式有何不同 定位。要查看差异,您需要使用 Chrome Canary 版(第 56 版) 或 Safari。

视差透视屏幕截图

Robert Flack 的演示,其中展示了如何 position: sticky 会影响视差滚动。

各种错误和解决方法

不过,和任何事情一样 平滑处理:

  • 粘性支持不一致。仍在以下国家/地区提供支持 Chrome、Edge 完全不支持此功能,而 Firefox 则会在将粘性广告与透视转换结合使用时发生绘制错误。在此类 在这种情况下,有必要添加一些代码,以便仅添加 position: sticky-webkit- 前缀的版本)(针对移动 Safari) 。
  • “效果不尽如人意”在 Edge 中。Edge 尝试在以下时间处理滚动操作: 操作系统级别,这通常是一件好事,但在这种情况下,它会阻止它 检测角度变化。要解决此问题,您可以添加 位置固定的元素,因为这似乎是将 Edge 切换为 非操作系统滚动方式, 并确保考虑了视角变化。
  • “页面内容变得异常大!”许多浏览器都考虑到了 在决定网页内容的大小时,Chrome 和 Safari 仍有遗漏 未考虑观点。因此 例如,对某个元素应用 3 倍的缩放值, 都可以看到滚动条等类似内容,即使元素在 已应用“perspective”。要解决此问题,可以采用 缩放元素(使用 transform-origin: bottom right),这样做是可行的,因为它会导致尺寸过大的元素成为 “否定区域”(通常为左上角)可滚动 区域不允许您查看或滚动到排除区域中的内容。

总结

谨慎使用视差是一种有趣的效果。如您所见, 实现高性能、滚动耦合、跨浏览器。 因为这需要一点数学修饰, 样板文件以获得所需效果,我们封装了一个小型帮助程序库 和示例,您可以在我们的界面元素示例 GitHub 代码库中找到它们。

来玩一把,告诉我们您的进展情况。