如果您希望 body 背景保持固定位置(在滚动时保持不变),有哪些选择?CSS 中的 background-attachment: fixed
最好情况下在移动浏览器中效果不佳,最坏情况下甚至不受最广泛使用的移动浏览器支持。您可以完全放弃这个想法,并使用媒体查询让背景在小屏幕上滚动。
或者使用一个小修复来解决。我想我们可以称之为“黑客”,因为它是一种代码中的变通方法,可以说我们根本不应该这样做。
问题
在我向您展示修复方法之前,让我们先检查一下问题。我们可以通过查看两种不同的 CSS 背景方法来了解它。
- 使用线性渐变的背景
- 使用图像的背景
线性渐变
我希望将背景渐变保持在滚动时的固定位置,因此让我们将基本的 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 上)的结果
渐变只是随着其他内容一起滚动,然后跳回。我不知道为什么会出现这种情况——也许当 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;
}
另一个需要注意的有趣之处是,当应用 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: fixed
与background-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%;
}
成功!
我们可以对背景图像使用相同的技巧黑客,它可以正常工作。但是,当 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
属性的元素一样,这在下面的示例中非常明显!只需观察视频中右边的条(紫色)。
即使 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 可以来救场.
不错的变通方法。
您是否尝试过将
background-attachment: fixed
应用于 html 元素本身?这应该可以说不受您使用的其他样式的影响;唯一的例外是 ‘position: fixed’ - **永远不要在根元素上使用它**。这样一来,您就可以自由地在 body 上使用其他与背景相关的样式,即使在速记中也是如此。
请注意,我还没有测试过。
祝您编程愉快!
哇,你真是救了我的命!希望你在接下来的24小时内能喝到一杯冰镇啤酒。干杯!
我的意思是写“其他样式”,而不是“其他与背景相关的样式”,在我之前的回复中。当然,所有“与背景相关的”样式都应该在同一个元素上。我需要咖啡。干杯。
我使用的一种方法似乎在桌面和移动设备上都能正常工作,就是将背景图像应用于伪元素,大小与内容匹配,如下所示
这在 `::after` 中也能工作。
我发现的唯一缺点是它占用了一个伪元素。
如果这是一个问题,因为你需要使用该伪元素来完成另一个目的,那么只需要使用一个嵌套的元素,其 CSS 与使用伪元素类似。
如果有其他方法也能达到同样的效果,我希望能了解。
感谢您的想法,我尝试将它付诸实践,但使用 fixed 定位似乎想要将根元素作为容器,即使直接父元素具有相对定位。
我一直使用 `::before` 而不是实际的元素来实现这一点。
请尝试在一行中使用 2 或 3 张图片。因此,文本 1 大于图像 1,应该在其中滚动。然后图像 2 应该出现并在其中滚动一个文本。可以说,这是一个包含图片的单页。
我无法仅使用 CSS 使它在超过 1 张图片的情况下工作。我仍在研究一个包含多个图片的解决方案,该解决方案依赖于 JS,但我很担心,即使在开始之前,它也可能没有很高的性能。如果有人有建议吗?
您好,感谢您提供这些信息。我是一个新手。请您解释一下,将代码放在 WordPress 主题编辑器的哪个位置?提前感谢您的帮助:)
这是一个很棒的方法,它还有个额外的好处,那就是允许我们在顶部和/或底部设置一个偏移量,以补偿固定标题和/或页脚,这意味着背景图像的顶部或底部部分仍然可见,不会被标题或页脚隐藏。
通常我不评论,但我一直在寻找一个解决移动设备 URL 地址栏调整大小问题的解决方案,我已经阅读了超过 100 篇关于该主题的文章,但没有一篇能解决这个问题。
所以请接受我真诚的“谢谢”。