当地址栏隐藏 iOS/Android/Mobile Chrome 时,背景图像会跳转

我正在开发一个使用 Bootstrap 的响应站点。

该网站在移动设备/平板电脑/桌面上有一个全屏幕背景图像,这些图像通过两个 div 旋转和淡出。

几乎完美,除了一个问题。使用 iOS Safari、 Android 浏览器或 Android 上的 Chrome,当用户向下滚动页面并导致地址栏隐藏时,背景会略微跳动。

网站在这里: http://lt2.daveclarke.me/

在移动设备上访问它,然后向下滚动,你会看到图像调整/移动。

我用于背景 DIV 的代码如下:

#bg1 {
background-color: #3d3d3f;
background-repeat:no-repeat;
background-attachment:fixed;
background-position:center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover; position:fixed;
width:100%;
height:100%;
left:0px;
top:0px;
z-index:-1;
display:none;
}

欢迎所有建议-这已经做了我的头一段时间! !

112004 次浏览

这个问题是由于 URL 栏收缩/滑出,并改变了 # bg1和 # bg2 div 的大小,因为它们是100% 高度并且是“固定的”。由于背景图像被设置为“覆盖”,它会随着包含区域的增大而调整图像的大小/位置。

基于网站的响应性质,背景必须缩放。我考虑了两种可能的解决方案:

1)将 # bg1,# bg2高度设置为100vh。理论上,这是一个优雅的解决方案。但是,iOS 有一个 vh bug (http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6/)。我尝试使用最大高度来防止这个问题,但它仍然存在。

2)由 Javascript 决定的视窗大小不受 URL 栏的影响。因此,可以使用 Javascript 根据 viewport 大小在 # bg1和 # bg2上设置静态高度。这不是最好的解决方案,因为它不是纯 CSS,并且在页面加载时有一个轻微的图像跳转。然而,考虑到 iOS 的“ vh”错误(在 iOS7中似乎没有修复) ,这是我看到的唯一可行的解决方案。

var bg = $("#bg1, #bg2");


function resizeBackground() {
bg.height($(window).height());
}


$(window).resize(resizeBackground);
resizeBackground();

顺便说一句,我在 iOS 和 Android 系统中看到了很多关于调整 URL 栏大小的问题。我理解其目的,但是他们真的需要考虑清楚他们给网站带来的奇怪的功能和破坏。最新的变化是,你不能再使用滚动技巧在 iOS 或 Chrome 上“隐藏”页面加载的 URL 栏。

编辑: 虽然上面的脚本可以很好地防止背景调整大小,但是当用户向下滚动时,它会造成一个明显的间隙。这是因为它将背景大小保持在屏幕高度的100% 减去 URL 栏。如果我们像瑞士人建议的那样在高度上增加60px,这个问题就解决了。这确实意味着当 URL 栏出现时,我们无法看到背景图像的底部60px,但是它可以防止用户看到空白。

function resizeBackground() {
bg.height( $(window).height() + 60);
}

我目前使用的解决方案是检查 $(document).ready上的 userAgent,看看它是否是一个违规的浏览器。如果是,请遵循以下步骤:

  1. 将相关高度设置为当前视窗高度,而不是“100%”
  2. 存储当前视图水平值
  3. 然后,在 $(window).resize上,只有当新的 水平视口维度与其初始值不同时,才更新相关的高度值
  4. 存储新的水平和垂直值

或者,您也可以测试只有当垂直调整大小超过地址栏的高度时才允许垂直调整大小。

对了,地址栏确实会影响 $(window).height。参见: 移动 Webkit 浏览器(chrome)地址栏更改 $(window)。高度() ; 使背景大小: 每次覆盖重新缩放 窗口宽度-高度 vs 屏幕宽度-高度?

我发现杰森的回答对我来说并不是很有效,我仍然感到很兴奋。Javascript 确保页面顶部没有空白,但是当地址栏消失/重新出现时,背景仍然会跳动。因此,除了 Javascript 修复之外,我还将 transition: height 999999s应用到 div。这将创建一个持续时间很长的转换,实际上会冻结元素。

当我试图创建一个可以覆盖整个视图的入口屏幕时,我也遇到了这个问题。不幸的是,公认的答案不再有效。

1)高度设置为 100vh的元素在每次视窗大小变化时都会调整大小,包括那些由出现 URL 栏(dis)引起的情况。

2) $(window).height()返回的值也受 URL 栏大小的影响。

一种解决方案是按照 作者: AlexKempton中的建议,使用 transition: height 999999s“冻结”元素。缺点是,这有效地禁用了对所有视窗大小变化的适应,包括那些由屏幕旋转引起的变化。

因此,我的解决方案是使用 JavaScript 手动管理视图更改。这使我能够忽略可能由 URL 栏引起的小的变化,而只对大的变化作出反应。

function greedyJumbotron() {
var HEIGHT_CHANGE_TOLERANCE = 100; // Approximately URL bar height in Chrome on tablet


var jumbotron = $(this);
var viewportHeight = $(window).height();


$(window).resize(function () {
if (Math.abs(viewportHeight - $(window).height()) > HEIGHT_CHANGE_TOLERANCE) {
viewportHeight = $(window).height();
update();
}
});


function update() {
jumbotron.css('height', viewportHeight + 'px');
}


update();
}


$('.greedy-jumbotron').each(greedyJumbotron);

编辑: 实际上我将这项技术与 height: 100vh一起使用。从一开始就正确地呈现页面,然后 javascript 启动并开始手动管理高度。这样在加载页面时(甚至在加载后)根本不会出现闪烁。

我的解决方案涉及到一些 javascript。在 div 上保持100% 或100vh (这样可以避免 div 不出现在初始页面加载中)。然后当页面加载时,获取窗口高度并将其应用到相关元素。避免跳跃,因为现在您的 div 上有一个静态高度。

var $hero = $('#hero-wrapper'),
h     = window.innerHeight;


$hero.css('height', h);

这个问题可以通过媒体查询和一些数学运算来解决:

@media (max-device-aspect-ratio: 3/4) {
height: calc(100vw * 1.333 - 9%);
}
@media (max-device-aspect-ratio: 2/3) {
height: calc(100vw * 1.5 - 9%);
}
@media (max-device-aspect-ratio: 10/16) {
height: calc(100vw * 1.6 - 9%);
}
@media (max-device-aspect-ratio: 9/16) {
height: calc(100vw * 1.778 - 9%);
}

由于当 URL 栏消失时 vh 将发生变化,因此需要用另一种方法确定高度。值得庆幸的是,viewport 的宽度是恒定的,而且移动设备只有几个不同的长宽比; 如果你可以确定宽度和长宽比,一个小小的数学就可以给你 vh 应该工作时的 viewport 高度。流程是这样的

1)创建一系列针对方面比率的媒体查询。

  • 使用设备长宽比代替长宽比,因为后者会在网址栏消失时调整大小

  • 我在设备长宽比中添加了“ max”,以便针对最流行的设备之间的任何长宽比。虽然不会那么精确,但是它们只适用于少数用户,而且仍然非常接近于正确的 vh。

  • 使用水平/垂直方式记住媒体查询,因此对于 Portait,需要翻转数字

2)对于每个媒体查询,无论你希望元素在 vw 中的垂直高度是多少百分比,都乘以高宽比的反向。

  • 由于您知道宽度和宽度与高度的比率,所以只需将所需的% (在您的示例中为100%)乘以高度/宽度的比率。

3)你必须确定网址栏的高度,然后从高度减去它。我还没有找到精确的测量,但我使用9% 的移动设备在景观,这似乎工作得相当不错。

这不是一个非常优雅的解决方案,但其他选项也不是很好,因为它们是:

  • 让用户觉得你的网站有问题,

  • 具有不适当大小的元素,或者

  • 使用 javascript 进行一些 基本的样式设计,

缺点是一些设备可能有不同的网址栏高度或长宽比最流行的。然而,使用这种方法,如果只有少数设备遭受几个像素的加减,这似乎比每个人都有一个网站调整大小时,我刷。

为了简单起见,我还创建了一个 SASS 混音:

@mixin vh-fix {
@media (max-device-aspect-ratio: 3/4) {
height: calc(100vw * 1.333 - 9%);
}
@media (max-device-aspect-ratio: 2/3) {
height: calc(100vw * 1.5 - 9%);
}
@media (max-device-aspect-ratio: 10/16) {
height: calc(100vw * 1.6 - 9%);
}
@media (max-device-aspect-ratio: 9/16) {
height: calc(100vw * 1.778 - 9%);
}
}

我用 javascript 设置了 width 元素。

Html

<div id="gifimage"><img src="back_phone_back.png" style="width: 100%"></div>

CSS

#gifimage {
position: absolute;
height: 70vh;
}

Javascript

imageHeight = document.getElementById('gifimage').clientHeight;


// if (isMobile)
document.getElementById('gifimage').setAttribute("style","height:" + imageHeight + "px");

我不使用 jquery etc,因为我希望它尽快加载。

这里的所有答案都使用 window高度,这个高度受 URL 栏的影响。大家都忘了 screen的高度了吗?

以下是我的 jQuery 解决方案:

$(function(){


var $w = $(window),
$background = $('#background');


// Fix background image jump on mobile
if ((/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera)) {
$background.css({'top': 'auto', 'bottom': 0});


$w.resize(sizeBackground);
sizeBackground();
}


function sizeBackground() {
$background.height(screen.height);
}
});

添加 .css()部分将不可避免地将顶部对齐的绝对定位元素改为底部对齐,因此根本没有跳转。尽管如此,我认为没有理由不直接将它添加到普通的 CSS 中。

我们需要用户代理嗅探器,因为桌面上的屏幕高度没有帮助。

而且,这都是假设 #background是一个填充窗口的固定位置元素。

对于 JavaScript 纯粹主义者(警告——未经测试) :

var background = document.getElementById('background');


// Fix background image jump on mobile
if ((/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera)) {
background.style.top = 'auto';
background.style.bottom = 0;


window.onresize = sizeBackground;
sizeBackground();
}


function sizeBackground() {
background.style.height = screen.height;
}

编辑: 对不起,这不能直接回答您的具体问题与一个以上的背景。但是,这是搜索固定背景在移动设备上跳转的问题时首次出现的结果之一。

我在我们网站的标题上也有类似的问题。

html, body {
height:100%;
}
.header {
height:100%;
}

这将结束在一个跳动的滚动体验机器人铬,因为。头-容器将重新缩放后,网址栏隐藏和手指是从屏幕上删除。

CSS-解决方案:

添加以下两行,将防止隐藏网址栏和垂直滚动仍然是可能的:

html {
overflow: hidden;
}
body {
overflow-y: scroll;
-webkit-overflow-scrolling:touch;
}

我创建了一个普通的 javascript 解决方案来使用 VH 单元。在几乎任何地方使用 VH 都会受到滚动条上地址栏最小化的影响。为了修复页面重绘时显示的 jank,我在这里使用了 js,它将使用 VH 单元(如果给它们类 .vh-fix)获取所有元素,并给它们内联的像素高度。基本上就是把它们冷冻到我们想要的高度。您可以在旋转或视窗大小变化时这样做,以保持响应。

var els = document.querySelectorAll('.vh-fix')
if (!els.length) return


for (var i = 0; i < els.length; i++) {
var el = els[i]
if (el.nodeName === 'IMG') {
el.onload = function() {
this.style.height = this.clientHeight + 'px'
}
} else {
el.style.height = el.clientHeight + 'px'
}
}

这解决了我所有的用例,希望能有所帮助。

这是我的解决方案,解决这个问题,我已经添加的代码直接注释。 我已经测试了这个解决方案,并且运行良好。希望我们对每个人都有同样的问题。

//remove height property from file css because we will set it to first run of page
//insert this snippet in a script after declarations of your scripts in your index


var setHeight = function() {
var h = $(window).height();
$('#bg1, #bg2').css('height', h);
};


setHeight(); // at first run of webpage we set css height with a fixed value


if(typeof window.orientation !== 'undefined') { // this is more smart to detect mobile devices because desktop doesn't support this property
var query = window.matchMedia("(orientation:landscape)"); //this is is important to verify if we put
var changeHeight = function(query) {                      //mobile device in landscape mode
if (query.matches) {                                    // if yes we set again height to occupy 100%
setHeight(); // landscape mode
} else {                                                //if we go back to portrait we set again
setHeight(); // portrait mode
}
}
query.addListener(changeHeight);                          //add a listner too this event
}
else { //desktop mode                                       //this last part is only for non mobile
$( window ).resize(function() {                           // so in this case we use resize to have
setHeight();                                            // responsivity resisizing browser window
});
};

这是最好的解决方案(最简单的)比我试过的所有方法都要好。

... 而这并不能保持本地经验的地址栏

例如,您可以使用 CSS 将高度设置为100% ,然后在加载文档时,使用 javascript 将窗口高度的 px 值设置为0.9 * 。

例如 jQuery:

$("#element").css("height", 0.9*$(window).height());

)

不幸的是,没有任何东西可以与纯 CSS 一起工作: P,但也要小心的是,vhvw在 iPhone 上是错误的-使用百分比。

我找到了一个不用 Javascript 的简单解决方案:

transition: height 1000000s ease;
-webkit-transition: height 1000000s ease;
-moz-transition: height 1000000s ease;
-o-transition: height 1000000s ease;

所有这一切只是延迟了运动,以至于它的速度慢得难以置信,以至于没有引起注意。

我们花了几个小时来理解这个问题的所有细节,并找出最佳的实现。事实证明,截至2016年4月,只有 iOS Chrome 存在这个问题。我试用了 Android Chrome 和 iOS Safari ——没有这个问题,两者都很好。因此,我使用的 iOS Chrome 的解决方案是:

$(document).ready(function() {
var screenHeight = $(window).height();
$('div-with-background-image').css('height', screenHeight + 'px');
});

我正在使用这个变通方法: 修正页面加载时 bg1的高度:

var BG = document.getElementById('bg1');
BG.style.height = BG.parentElement.clientHeight;

然后在“窗口”上附加一个调整事件侦听器,该侦听器执行以下操作: 如果调整大小后的高度差小于60px (任何超过 url 条高度的值) ,那么什么都不做,如果大于60px,那么再次将 bg1的高度设置为其父级的高度!完整代码:

window.addEventListener("resize", onResize, false);
var BG = document.getElementById('bg1');
BG.style.height = BG.parentElement.clientHeight;
var oldH = BG.parentElement.clientHeight;


function onResize() {
if(Math.abs(oldH - BG.parentElement.clientHeight) > 60){
BG.style.height = BG.parentElement.clientHeight + 'px';
oldH = BG.parentElement.clientHeight;
}
}

附言: 这只虫子真烦人!

类似于@AlexKempton 的回答

我一直在使用长的转换延迟来防止元素调整大小。

例如:

transition: height 250ms 600s; /*10min delay*/

这样做的折衷方案是防止元素的大小调整,包括在设备旋转时,但是,如果这是一个问题,您可以使用一些 JS 来检测 orientationchange并重置高度。

对于那些想要在 Chrome 移动浏览器将地址栏从显示变为隐藏,反之亦然的过程中听到窗口的实际内部高度和垂直滚动的人来说,我找到的唯一解决方案是设置一个间隔函数,并测量 window.innerHeight与之前值的差异。

这里引入了以下代码:

var innerHeight = window.innerHeight;
window.setInterval(function ()
{
var newInnerHeight = window.innerHeight;
if (newInnerHeight !== innerHeight)
{
var newScrollY = window.scrollY + newInnerHeight - innerHeight;
// ... do whatever you want with this new scrollY
innerHeight = newInnerHeight;
}
}, 1000 / 60);

我希望这个能派上用场。有人知道更好的解决办法吗?

遇到类似问题时,我想到的解决方案是每次触发 touchmove事件时都将元素高度设置为 window.innerHeight

var lastHeight = '';


$(window).on('resize', function () {
// remove height when normal resize event is fired and content redrawn
if (lastHeight) {
$('#bg1').height(lastHeight = '');
}
}).on('touchmove', function () {
// when window height changes adjust height of the div
if (lastHeight != window.innerHeight) {
$('#bg1').height(lastHeight = window.innerHeight);
}
});

这使得元素在任何时候都可以精确地覆盖可用屏幕的100% ,不多也不少。即使在地址栏隐藏或显示期间。

然而,遗憾的是,我们必须拿出这样的补丁,其中简单的 position: fixed应该工作。

简单的回答,如果你的背景允许,为什么不设置背景大小: 到一些只覆盖设备宽度的媒体查询和使用一起: 之后的位置: 固定; 黑客 给你

背景大小: 901px; 对于任何 < 900px 的屏幕?不完美或响应,但工作的魅力为我在手机 < 480px,因为我使用的是一个抽象的 BG。

有了 iOS 中 CSS 自定义属性(变量)的支持,您可以用 JS 设置这些属性并仅在 iOS 上使用它们。

const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if (iOS) {
document.body.classList.add('ios');
const vh = window.innerHeight / 100;
document.documentElement.style
.setProperty('--ios-10-vh', `${10 * vh}px`);
document.documentElement.style
.setProperty('--ios-50-vh', `${50 * vh}px`);
document.documentElement.style
.setProperty('--ios-100-vh', `${100 * vh}px`);
}
body.ios {
.side-nav {
top: var(--ios-50-vh);
}
section {
min-height: var(--ios-100-vh);
.container {
position: relative;
padding-top: var(--ios-10-vh);
padding-bottom: var(--ios-10-vh);
}
}
}

我的答案是,每个来到这里的人(像我一样)都会找到一个解决方案,解决由隐藏地址裸/浏览器界面引起的 bug。

隐藏地址栏会触发 resize-event。但不同于其他调整大小的事件,如切换到横向模式,这不会改变窗口的宽度。因此,我的解决方案是挂接到调整事件,并检查宽度是否相同。

// Keep track of window width
let myWindowWidth = window.innerWidth;


window.addEventListener( 'resize', function(event) {


// If width is the same, assume hiding address bar
if( myWindowWidth == window.innerWidth ) {
return;
}


// Update the window width
myWindowWidth = window.innerWidth;


// Do your thing
// ...
});

删除 position: fixed;position: absolute;,如果你有任何他们在你的背景 div,为我解决了这个问题。