如果你想让主体背景处于一个固定的位置,在滚动时保持不动,你有什么选择呢?background-attachment: fixed ,在最好的情况下,在移动浏览器中工作得不好,在最坏的情况下,甚至不被最广泛使用的移动浏览器支持。你可以完全放弃这个想法,使用媒体查询让背景在小屏幕上滚动。
或者通过一个小的修正来解决这个问题。我想我们可以称它为 "黑客",因为这是一个可以说是我们根本不需要做的代码中的变通。
这个问题
在我向你展示修复方法之前,让我们先看看这个问题。我们可以通过观察两种不同的CSS背景方法来了解它:
- 使用线性渐变的背景
- 使用图像的背景
线性渐变
我想让背景渐变在滚动时保持在一个固定的位置,所以让我们在正文中应用基本的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中的结果,都是在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 计算了一个相对于视口的固定背景位置。
即使我们说主体是100vh,background-attachment: fixed 也不完全与之相符。奇怪!也许原因是,background-attachment: fixed 依赖于最小的视口,而元素依赖最大的视口。David Bokan解释说。
以视口单位定义的长度(即
vh)将不会因URL栏的显示或隐藏而调整大小。相反,vh单位将被确定为视口高度,就像URL栏总是隐藏的一样。也就是说,vh单位将被调整到 "最大可能的视口"。这意味着当URL栏显示时,100vh将大于可见高度。
这些问题在caniuse上有很好的记录:
- 火狐似乎不支持应用于文本区域元素的本地值。
- 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在他的文章中指出。
也就是说,一个
position: fixed,其包含的块是ICB的元素将响应URL栏的显示或隐藏而调整大小。例如,如果它的高度是100%,它将总是正好填满可见的高度,无论URL栏是否显示。同样,对于vh,它们也将调整大小以匹配可见高度,并考虑到URL栏的位置。
如果我们考虑到最后一句话,这里的情况似乎不是这样的。有固定定位和100vh高度的元素,无论URL栏是否显示,都不会改变其高度。事实上,高度是根据 "最大可能的视口 "的高度来确定的。这在下面的例子中很明显。只需观察视频中的浅蓝色条。
因此,看起来,当与一个100vh的容器一起工作时,background-attachment: fixed 考虑最小可能的视口高度,而一般的元素考虑最大可能的视口高度。
例如,当需要重绘时,background-attachment: fixed 就会简单地停止工作,比如当移动浏览器的地址栏在滚动时消失。浏览器根据最大可能的视口(现在实际上是最小可能的视口,因为URL栏被隐藏了)来调整背景,浏览器没有足够的效率来即时重绘,这导致了严重的滞后。
我们的黑客通过使背景成为一个元素而不是一个实际的背景来解决这个问题。我们给包含内容的元素一个绝对位置,将其堆叠在包含图像的元素之上,然后在后者上应用一个固定位置。嘿,成功了
注意,视口高度的计算不包括底部的导航栏(如果存在的话)。下面是Chrome Android中底部导航栏存在与否的比较。


有什么坏处吗?也许吧!我们使用的是一般的。我们使用的是一般的<div> ,而不是实际的<img> 标签,所以我不会说这个标记是语义的。而这可能会导致可访问性问题。如果你正在处理一个为内容增加意义或背景的图像,那么<img> 是正确的方式,利用适当的alt ,为屏幕阅读器提供描述。
但是,如果我们走适当的<img> 路线,那么我们就会回到我们开始的地方。另外,如果你在底部有一个导航栏,它也会自动隐藏自己,那么我也无能为力。如果黑客不能解决这个问题,那么也许JavaScript可以来拯救你。