固定背景附件黑客

Avatar of Murtuzaali Surti
Murtuzaali Surti

DigitalOcean 为您的旅程的每个阶段提供云产品。立即开始使用 价值 200 美元的免费积分!

如果您希望 body 背景保持固定位置(在滚动时保持不变),有哪些选择?CSS 中的 background-attachment: fixed 最好情况下在移动浏览器中效果不佳,最坏情况下甚至不受最广泛使用的移动浏览器支持。您可以完全放弃这个想法,并使用媒体查询让背景在小屏幕上滚动。

或者使用一个小修复来解决。我想我们可以称之为“黑客”,因为它是一种代码中的变通方法,可以说我们根本不应该这样做。

问题

在我向您展示修复方法之前,让我们先检查一下问题。我们可以通过查看两种不同的 CSS 背景方法来了解它。

  1. 使用线性渐变的背景
  2. 使用图像的背景

线性渐变

我希望将背景渐变保持在滚动时的固定位置,因此让我们将基本的 CSS 样式应用于 body,这正是它所做的。

body {
  background: linear-gradient(335deg, rgba(255,140,107,1) 0%, rgba(255,228,168,1) 100%);
  background-attachment: fixed;
  background-position: center;
  background-repeat: no-repeat;
  height: 100vh;
}

以下是 Chrome 和 Firefox(分别在 Android 上)的结果

Chrome Android
Firefox Android

渐变只是随着其他内容一起滚动,然后跳回。我不知道为什么会出现这种情况——也许当 URL 选项卡在滚动时向上或消失,并且浏览器难以实时重新渲染渐变时?这是我最好的猜测,因为它似乎只发生在移动浏览器中。

如果您想知道 iOS Safari 的情况,我还没有在 iOS 上亲自测试,但这个问题也存在。一些 人已经报告了这个问题,并且它似乎表现类似。

背景图像

图像的这个问题没有什么不同。

body {
  background: url(../assets/test_pic.jpg);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  height: 100vh;
}
顶部的灰色部分只是表示 Chrome Android 中实际存在 URL 栏。

另一个需要注意的有趣之处是,当应用 background-attachment: fixed 时,即使我们明确指定,高度也会被忽略。这是因为 background-attachment 相对于视窗计算固定背景位置。

即使我们说 body 是 100vh,background-attachment: fixed 也并不完全符合它。很奇怪!也许原因是 background-attachment: fixed 依赖于最小的视窗,而元素依赖于最大的视窗。 David Bokan 解释说,

在视窗单位(即 vh)中定义的长度不会因显示或隐藏 URL 栏而重新调整大小。相反,vh 单位的大小将根据视窗高度调整,就好像 URL 栏始终隐藏一样。也就是说,vh 单位的大小将根据“最大可能的视窗”调整。这意味着 100vh 在显示 URL 栏时将大于可见高度。

这些问题在caniuse 上有很好的记录

  • Firefox 在应用于 textarea 元素时似乎不支持 local 值。
  • Chrome 存在一个问题,当对也定义了 background-attachment: fixed 的选择器使用 will-change 属性时会发生。它会导致图像被切断并在周围获得空白。
  • iOS 存在一个问题,阻止将 background-attachment: fixedbackground-size: cover 一起使用。

让我们修复它

如果愿意,可以称之为临时黑客。你们中的一些人可能已经尝试过它。无论如何,它修复了我们刚才看到的线性渐变和背景图像问题。

因此,如您所知,我们遇到了 background-attachment: fixed 属性的麻烦,并且您可能已经猜到,我们将从代码中删除它。如果它正在查看最小可能的视窗,那么也许我们应该使用一个寻找最大可能的视窗并定位它的元素。

因此,我们创建了两个独立的元素——一个用于 background-gradient,另一个用于其余内容。我们用 position: fixed 替换了 background-attachment: fixed

<div class="bg"></div>
<div class="content">
  <!-- content -->
</div>
.bg {
  background: linear-gradient(335deg, rgba(255,140,107,1) 0%, rgba(255,228,168,1) 100%);
  background-repeat: no-repeat;
  background-position: center;
  height: 100vh;
  width: 100vw;
  position: fixed;
  /* z-index usage is up to you.. although there is no need of using it because the default stack context will work. */
  z-index: -1; // this is optional
}

现在,将其余内容(除了包含背景图像的元素)包装在主容器中。

.content{
  position: absolute;
  margin-top: 5rem;
  left: 50%; 
  transform: translateX(-50%);
  width: 80%;
}

成功!

Chrome Android
Firefox Android

我们可以对背景图像使用相同的技巧黑客,它可以正常工作。但是,当 URL 栏隐藏自身时,您确实会看到某种背景滚动,但白色补丁不再存在。

.img {    
  background: url('../assets/test_pic.jpg');
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  position: fixed;
  height: 100vh;
  width: 100vw;
}

.content {
  position: absolute;
  left: 50%; 
  margin-top: 5rem;
  transform: translateX(-50%);
  width: 80%;
}
Chrome Android
Firefox Android

以下是我的一些感悟

一个固定定位元素,其高度设置为 100%,表现得就像具有 background-attachment: fixed 属性的元素一样,这在下面的示例中非常明显!只需观察视频中右边的条(紫色)。

正在测试的网站来自这篇文章.

即使 David Bokan 在他的 文章 中指出

也就是说,一个包含块是 ICB 的 position: fixed 元素会根据显示或隐藏 URL 栏而重新调整大小。例如,如果它的高度是 100%,它将始终完全填充可见高度,无论 URL 栏是否显示。类似地,对于 vh 长度,它们也会调整大小以匹配可见高度,并考虑 URL 栏位置。

如果我们考虑最后一句话,这里似乎并非如此。具有固定定位和 100vh 高度的元素不会改变它们的高度,无论 URL 栏是否显示。事实上,高度是根据“最大可能的视窗”的高度计算的。这在下面的示例中很明显。只需观察视频中浅蓝色的条。

正在测试的网站来自这篇文章.

因此,似乎在使用 100vh 容器时,background-attachment: fixed 考虑的是最小可能的视窗高度,而元素通常会考虑最大可能的视窗高度。

例如,当需要重绘时(例如,当移动浏览器的地址栏在滚动时消失时),background-attachment: fixed 根本无法工作。浏览器会根据最大可能的视窗(实际上现在是最小的视窗,因为 URL 栏隐藏了)调整背景,并且浏览器效率不够高,无法实时重绘,这会导致严重的延迟。

我们的黑客通过将背景作为元素而不是实际背景来解决这个问题。我们给包含内容的元素一个绝对位置,将其堆叠在包含图像的元素之上,然后对后者应用固定位置。嘿,它有效!

请注意,视窗高度的计算不包括底部的导航栏(如果存在)。以下是 Chrome Android 中底部有和没有导航栏时的比较。

有缺点吗?也许吧!我们正在使用一个普通的 <div>,而不是一个真正的 <img> 标签,所以我不会说标记是语义化的。这会导致 可访问性问题。如果您正在使用一个为内容添加意义或上下文的图像,那么 <img> 是正确的方式,利用适当的 alt 描述供屏幕阅读器使用。

但如果我们走正确的 <img> 路线,那么我们就回到了起点。此外,如果您在底部有一个导航栏,它也会自动隐藏,那么我无能为力。如果黑客无法解决,那么也许JavaScript 可以来救场.