The Fixed Background Attachment Hack

657 阅读6分钟

The Fixed Background Attachment Hack

如果您希望身体背景处于固定位置(放置在滚动中),您有什么选择? background-attachment: fixed在CSS中,这个在移动浏览器中效果不好,在最坏的情况下甚至没有得到使用最广泛的移动浏览器的支持。您可以完全抛弃这个想法,让背景使用媒体查询在小屏幕上滚动。

或者用一个小的修复绕过它。我想我们可以称之为 "hack", 因为它是代码中的解决方法, 可以说我们根本不应该这样做。

问题

在我告诉你修复方法之前,让我们来研究一下这个问题。我们可以通过查看两种不同的方法来了解 CSS 背景:

  1. 使用线性梯度的背景
  2. 使用图像的背景

线性梯度

我想将背景渐变保持在滚动的固定位置,因此,让我们将基本的 CSS 造型应用于执行该操作的主体:

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 的结果:

梯度只需与其他内容一起滚动,然后跳回。我不知道这是为什么 - 也许当 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 安卓系统中存在实际 URL 条。

另一个有趣的事情是,当应用background-attachment: fixed时,高度被忽略,即使我们明确指定它。这是因为background-attachment计算相对于视场的固定背景位置。

即使我们说主体是100vh,background-attachment: fixed也不完全符合它。奇怪!也许原因background-attachment: fixed是依赖于尽可能小的视图波特,而元素依赖于最大的可能视口。大卫·博坎解释道

视距单元(即vh)中定义的长度不会因显示或隐藏的 URL 条而调整大小。相反,vh单元的大小将调整到视场高度,就好像网址栏始终隐藏一样。即,vh单元将大小为"最大可能的视图港"。这意味着当网址条显示时,100vh将大于可见高度。

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

  • 当应用于文本区域元素时,火狐似乎不支持本地值。
  • Chrome 在选择器上使用will-change属性时会出现问题,该选择器也已定义background-attachment: fixed。它会导致图像被切断,并获得周围的空白。
  • iOS 有一个问题,阻止background-attachment: fixedbackground-size: cover一起被使用

让我们来修复它

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

所以,如你所知,我们遇到了麻烦的background-attachment: fixed属性,你可能已经猜到,我们正在从我们的代码中删除它。 如果它正在查看 smallest 可能的视口,那么也许我们应该使用一个元素来查找 largest 可能的视口并将其定位。

因此,我们创建了两个单独的元素——一个用于background-gradient,另一个用于其余内容。 我们正在将background-attachment: fixed 替换为 position: 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%;
} 

成功!

我们可以使用相同的技巧兼容与背景图像,它的工作原理很好。但是,当 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%;
} 
 

这是我的要点

高度设定为 100% 的固定位置元素的行为与background-attachment: fixed属性元素一样,这在下面的示例中显而易见!只需观察视频中最右侧的条形图(紫色)。

正在测试的网站取自本文

尽管如此,大卫·博坎在他的文章中说:

即,position: fixed其包含块为 ICB 的元素将针对显示或隐藏的 URL 条进行调整大小。例如,如果它的高度是100%它总是会填补确切可见的高度,无论是否显示网址栏。同样,对于vh长度,它们也将调整大小,以匹配可见高度,同时考虑 URL 条位置。

如果我们考虑到最后一句话,这里似乎并非如此。具有固定定位和 100vh 高度的元素不会改变其高度,无论是否显示 URL 条。事实上,高度是根据"最大可能的视图港"的高度。这在下面的示例中显而易见。只需观察视频中的浅蓝色条形。

正在测试的网站取自本文

因此,在处理 100vh 的容器时,background-attachment: fixed似乎会考虑尽可能小的视场高度,而元素一般会考虑尽可能大的视场高度。

例如,background-attachment:fixed 只是在需要重绘时停止工作,比如移动浏览器的地址栏在滚动时消失。 浏览器根据最大的可能视场(事实上,现在隐藏的 URL 条是最小的视场)调整背景,浏览器的效率不足以在飞行中重新涂漆,从而导致重大滞后。

我们的黑客解决这个问题,使背景的元素,而不是,嗯,一个实际的背景。我们给包含内容的元素一个绝对位置,将其堆叠在包含图像的元素之上,然后在后者上应用固定位置。嘿, 成功了!

请注意,查看口高度的计算不包括底部的导航栏(如果存在)。以下是 Chrome Android 底部导航栏的存在与缺失之间的比较。

有什么缺点吗? 也许! 我们使用通用的 <div> 而不是实际的 <img> 标签,所以我不会说标记是语义的。 这可能会导致 可访问性问题。 如果您正在处理为内容添加含义或上下文的图像,那么使用 <img> 是正确的方法,为屏幕阅读器使用适当的 alt 描述。

但是,如果我们走正确的路线,那么我们就回到了我们开始的地方。此外, 如果你有一个导航栏在底部, 太自动隐藏自己, 那么我不能帮助它。如果黑客不会削减它,那么也许JavaScript可以来解决<img>