Фоновые изображения перескакивают, когда адресная строка скрывает iOS/Android/Mobile Chrome

В настоящее время я разрабатываю отзывчивый сайт, используя Twitter Bootstrap.

Сайт имеет полноэкранное фоновое изображение на мобильном устройстве/планшете/рабочем столе. Эти изображения вращаются и исчезают через каждый, используя два div.

Это почти идеально, за исключением одной проблемы. Используя iOS Safari, Android Browser или Chrome на Android, фон слегка перескакивает, когда пользователь прокручивает страницу вниз и вызывает скрытие адресной строки.

Сайт находится здесь: 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;
}

Все предложения приветствуются - это немного помолодело!

Ответ 1

Эта проблема вызвана сокращением/сдвигом строк URL-адреса и изменением размера div # bg1 и # bg2, поскольку они являются 100% -ной высотой и "фиксированными". Поскольку фоновое изображение установлено на "обложка", оно будет регулировать размер/положение изображения по мере того, как область содержимого больше.

Основываясь на отзывчивом характере сайта, фон должен масштабироваться. Я принимаю два возможных решения:

1) Установите высоту # bg1, # bg2 до 100vh. Теоретически это элегантное решение. Однако у iOS есть ошибка vh (http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6/). Я попытался использовать максимальную высоту, чтобы предотвратить проблему, но она осталась.

2) Размер видового экрана, определяемый Javascript, не зависит от строки URL. Поэтому Javascript можно использовать для установки статической высоты на # bg1 и # bg2 на основе размера видового экрана. Это не лучшее решение, так как это не чистый CSS, и есть небольшой скачок изображения при загрузке страницы. Тем не менее, это единственное жизнеспособное решение, которое я вижу с учетом ошибок iOS "vh" (которые, как представляется, не исправлены в iOS 7).

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

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

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

На стороне примечания, я видел так много проблем с этими барами изменения размера в iOS и Android. Я понимаю цель, но им действительно нужно продумать странную функциональность и хаос, которые они приносят на веб-сайты. Последнее изменение: вы больше не можете "скрывать" строку URL-адреса при загрузке страницы в iOS или Chrome с помощью трюков прокрутки.

EDIT: Несмотря на то, что приведенный выше script отлично работает для того, чтобы не изменять размер фона, это приводит к заметному разрыву, когда пользователи прокручиваются вниз. Это связано с тем, что он сохраняет фоновый размер на 100% от высоты экрана за вычетом строки URL. Если мы добавим 60 пикселей на высоту, как предполагает швейцар, эта проблема исчезнет. Это означает, что мы не увидим нижний 60px фонового изображения, когда панель URL присутствует, но она не позволяет пользователям видеть разрыв.

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

Ответ 2

Я обнаружил, что ответ Джейсона не совсем работает для меня, и я все еще получаю прыжок. Javascript гарантировал отсутствие пробела в верхней части страницы, но фон все еще прыгал, когда адресная строка исчезла/снова появилась. Так же как и исправление Javascript, я применил transition: height 999999s к div. Это создает переход с такой продолжительностью, что он фактически замораживает элемент.

Ответ 3

У меня такая же проблема в заголовке нашего сайта.

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

Это закончится скачкообразным прокруткой на андроидном хроме, потому что контейнер .header будет перемасштабироваться после того, как скрыта строка url-bar, и палец будет удален с экрана.

CSS-Решение:

Добавление следующих двух строк позволит предотвратить скрытие скрытых ссылок и вертикальной прокрутки:

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

Ответ 4

Проблема может быть решена с помощью медиа-запроса и некоторой математики. Здесь решение для ориентации portait:

@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%);
}

Так как vh изменится, когда строка url исчезнет, ​​вам нужно определить высоту другим способом. К счастью, ширина окна просмотра является постоянной, а мобильные устройства доступны только в нескольких разных пропорциях; если вы можете определить ширину и соотношение сторон, небольшая математика даст вам высоту видового экрана точно так же, как и vh. Здесь процесс

1) Создайте серию медиа-запросов для пропорций, которые вы хотите настроить.

  • используйте соотношение сторон устройства, а не коэффициент сжатия, потому что последний будет изменять размер, когда строка url исчезнет

  • Я добавил "max" к соотношению сторон устройства к цели любых пропорций, которые следуют между самыми популярными. Это будет не так точно, но они будут только для меньшинства пользователей и все равно будут близки к правильному vh.

  • помните медиа-запрос, используя горизонтальный/вертикальный, поэтому для portait вам нужно перевернуть числа

2) для каждого мультимедийного запроса умножайте любой процент вертикальной высоты, который вы хотите, чтобы элемент находился в vw по обратному отношению к формату.

  • Поскольку вы знаете ширину и отношение ширины к высоте, вы просто умножаете% вы хотите (100% в вашем случае) на отношение высоты/ширины.

3) Вы должны определить высоту строки url, а затем минус это с высоты. Я не нашел точных измерений, но я использую 9% для мобильных устройств в ландшафте и, похоже, работает достаточно хорошо.

Это не очень изящное решение, но другие варианты тоже не очень хорошие, учитывая, что они:

  • Если ваш веб-сайт выглядит неудовлетворительным для пользователя,

  • с элементами неправильного размера или

  • Использование javascript для базового стиля,

Недостатком является то, что некоторые устройства могут иметь разную высоту или пропорции url, чем самые популярные. Однако, используя этот метод, если только небольшое количество устройств страдает добавлением/вычитанием нескольких пикселей, это кажется мне намного лучше, чем все, у кого есть изменение размера сайта при прокрутке.

Чтобы сделать это проще, я также создал SASS mixin:

@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%);
  }
}

Ответ 5

Все ответы здесь используют высоту 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;
}

EDIT: Извините, что это напрямую не отвечает на вашу конкретную проблему более чем одним фоном. Но это один из первых результатов при поиске этой проблемы фиксированных фонов, прыгающих на мобильных устройствах.

Ответ 6

Я столкнулся с этой проблемой, когда пытался создать входной экран, который будет охватывать весь видовой экран. К сожалению, принятый ответ больше не работает.

1) Элементы с высотой, заданной до 100vh каждый раз при изменении размера видового экрана, в том числе в тех случаях, когда это вызвано (dis) появлением строки URL.

2) $(window).height() возвращает значения, также $(window).height() от размера строки URL.

Одним из решений является "замораживание" элемента с использованием transition: height 999999s как было предложено в ответе AlexKempton. Недостатком является то, что это эффективно отключает адаптацию ко всем изменениям размеров видового экрана, в том числе вызванным вращением экрана.

Поэтому мое решение заключается в управлении изменениями в видовом режиме с помощью 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);

EDIT: Я использую эту технику вместе с height: 100vh. Страница отображается правильно с самого начала, а затем javascript запускается и начинает управлять высотой вручную. Таким образом, во время загрузки страницы (или даже после нее) нет мерцания.

Ответ 7

Решение, которое я сейчас использую, - проверить userAgent на $(document).ready, чтобы увидеть, является ли это одним из злоумышленников. Если это так, выполните следующие действия:

  • Установите соответствующие высоты для текущей высоты окна просмотра, а не "100%"
  • Сохранить текущее горизонтальное значение окна просмотра
  • Затем, на $(window).resize, обновите соответствующие значения высоты, если новое измерение горизонтального видового экрана отличается от начального значения
  • Сохранить новые горизонтальные и вертикальные значения

При желании вы также можете проверить разрешающие вертикальные размеры только тогда, когда они превышают высоту адресной строки.

О, и адресная строка влияет на $(window).height. См. Изменение адресной строки браузера веб-браузера (хром) $(window).height(); создание фонового размера: масштабирование по каплям каждый раз JS "Окно" width-height vs "screen" width-height?

Ответ 8

Я нашел очень простое решение без использования Javascript:

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

Все это делает задержку движения так, что это невероятно медленно, что это не заметно.

Ответ 9

В моем решении задействовано несколько javascript. Держите 100% или 100vh на div (это позволит избежать того, что div не появляется при загрузке начальной страницы). Затем, когда страница загружается, возьмите высоту окна и примените ее к рассматриваемому элементу. Избегает перехода, потому что теперь у вас есть статическая высота на вашем div.

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

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

Ответ 10

Я создал ванильное 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'
  }
}

Это решило все мои варианты использования, надеюсь, что это поможет.

Ответ 11

Это мое решение для решения этой проблемы, я добавил комментарии непосредственно к коду. Я протестировал это решение и отлично работал. Надеюсь, что мы будем полезны для всех, у кого есть та же проблема.

//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
  }); 
};

Ответ 12

Это лучшее решение (простейшее) выше всего, что я пробовал.

... И это не сохраняет собственный интерфейс адресной строки!

Вы можете установить высоту с CSS на 100%, например, а затем установить высоту в 0.9 * высоты окна в px с javascript, когда документ будет загружен.

Например, с помощью jQuery:

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

)

К сожалению, нет ничего, что работает с чистым CSS: P, но также должно быть minfull, что vh и vw являются ошибками на iPhone - используйте проценты.

Ответ 13

Я устанавливаю свой элемент width с помощью javascript. После того, как я установил de vh.

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 ect. потому что я хочу, чтобы он загрузился как можно скорее.

Ответ 14

Потребовалось несколько часов, чтобы понять все детали этой проблемы и выяснить оптимальную реализацию. Оказывается, с апреля 2016 года только у iOS Chrome есть эта проблема. Я пробовал на Android Chrome и iOS Safari - оба были в порядке без этого исправления. Итак, исправление для iOS Chrome, которое я использую, это:

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

Ответ 15

Я использую этот обходной путь: Исправьте высоту bg1 при загрузке страницы:

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

Затем присоедините прослушиватель событий изменения размера к окну, который делает следующее: если разница в высоте после изменения размера меньше 60 пикселей (что-то большее, чем высота строки url), ничего не делайте и если оно больше 60 пикселей, тогда установите высоту 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;
    }
}

PS: Эта ошибка настолько раздражает!

Ответ 16

Как и ответ @AlexKempton. (не мог прокомментировать извинения)

Я использовал длинные задержки перехода, чтобы предотвратить изменение размера элемента.

например:

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

Компромисс с этим заключается в том, что он предотвращает изменение размера элемента, включая поворот устройства, однако вы можете использовать JS для обнаружения orientationchange и reset высоты, если это было проблемой.

Ответ 17

Для тех, кто хочет прослушать фактическую внутреннюю высоту и вертикальную прокрутку окна, в то время как браузер Chrome для мобильных устройств переводит строку URL из показанного в скрытое и наоборот, единственным решением, которое я нашел, является установка интервала функцию и измерить несоответствие 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);

Ответ 18

Решение, с которым я столкнулся, с аналогичной проблемой состоял в том, чтобы установить высоту элемента в window.innerHeight каждый раз при запуске события touchmove.

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.

Ответ 19

Простой ответ, если ваш фон позволяет, почему бы не установить background-size: что-то, просто покрывающее ширину устройства медиа-запросами и использовать вместе с: after position: fixed; взломать здесь.

IE: background-size: 901px; для любых экранов <900 пикселей? Не идеальный или отзывчивый, но на мобильном мне показалось обаяние <480px, так как я использую абстрактную фоновую музыку.