La imagen de fondo salta cuando la barra de direcciones oculta iOS / Android / Mobile Chrome


Actualmente estoy desarrollando un sitio responsivo usando Twitter Bootstrap.

El sitio tiene una imagen de fondo de pantalla completa en el móvil/tableta/escritorio. Estas imágenes giran y se desvanecen a través de cada una, utilizando dos divs.

Es casi perfecto, excepto una cuestión. Al usar iOS Safari, Android Browser o Chrome en Android, el fondo salta ligeramente cuando un usuario se desplaza por la página y hace que la barra de direcciones se oculte.

El sitio está aquí: http://lt2.daveclarke.me /

Visítelo en un dispositivo móvil y desplácese hacia abajo y debería ver el cambio de tamaño/movimiento de la imagen.

El código que estoy usando para el DIV de fondo es el siguiente:

#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;
}

Todas las sugerencias bienvenidas - ¡esto ha estado haciendo mi cabeza por un tiempo!!

Author: Dave Clarke, 2014-07-25

18 answers

Este problema es causado por las barras de URL que se encogen/se deslizan fuera del camino y cambian el tamaño de los divs #bg1 y #bg2 ya que son 100% de altura y "fijos". Dado que la imagen de fondo está configurada en "cubrir", ajustará el tamaño/posición de la imagen a medida que el área contenedora sea más grande.

Basado en la naturaleza responsiva del sitio, el fondo debe escalar. Tengo dos posibles soluciones:

1) Establezca la altura #bg1, #bg2 en 100vh. En teoría, esta es una solución elegante. Obstante, iOS tiene un error vh ( http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6 / ). Intenté usar una altura máxima para evitar el problema, pero se mantuvo.

2) El tamaño de la ventana, cuando está determinado por Javascript, no se ve afectado por la barra de URL. Por lo tanto, Javascript se puede utilizar para establecer una altura estática en el #bg1 y #bg2 basado en el tamaño de la ventana. Esta no es la mejor solución, ya que no es CSS puro y hay un ligero salto de imagen en la carga de la página. Sin embargo, es la única solución viable que veo teniendo en cuenta los errores "vh" de iOS (que no parecen ser corregidos en iOS 7).

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

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

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

En una nota al margen, he visto muchos problemas con estas barras de URL de redimensionamiento en iOS y Android. Entiendo el propósito, pero realmente necesitan pensar en la extraña funcionalidad y el caos que traen a los sitios web. El último cambio, es que ya no puede "ocultar" la barra de URL en la carga de la página en iOS o Chrome utilizando trucos de desplazamiento.

EDITAR: Mientras el script anterior funciona perfectamente para evitar que el fondo cambie de tamaño, causa una brecha notable cuando los usuarios se desplazan hacia abajo. Esto se debe a que mantiene el tamaño del fondo al 100% de la altura de la pantalla menos la barra de URL. Si añadimos 60px a la altura, como sugiere Swiss, este problema desaparece. Significa que no podemos ver los 60px inferiores de la imagen de fondo cuando la barra de URL está presente, pero evita que los usuarios vean una brecha.

function resizeBackground() {
    bg.height( $(window).height() + 60);
}
 95
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-11 12:14:31

Encontré que la respuesta de Jason no estaba funcionando para mí y todavía estaba recibiendo un salto. El Javascript aseguró que no hubiera espacio en la parte superior de la página, pero el fondo seguía saltando cada vez que la barra de direcciones desaparecía/reaparecía. Así que, además de la corrección de Javascript, apliqué transition: height 999999s al div. Esto crea una transición con una duración tan larga que prácticamente congela el elemento.

 58
Author: AlexKempton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-26 13:31:29

Tengo un problema similar en un encabezado de nuestro sitio web.

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

Esto terminará en una experiencia de desplazamiento nervioso en Android Chrome, porque el .header-container volverá a escalar después de que la barra url se oculte y el dedo se elimine de la pantalla.

CSS-Solución:

Agregando las siguientes dos líneas, evitará que la barra url se oculte y el desplazamiento vertical aún sea posible:

html {
    overflow: hidden;
}
body {
    overflow-y: scroll;
    -webkit-overflow-scrolling:touch;
}
 21
Author: mat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-31 18:56:36

El problema se puede resolver con una consulta de medios y algunas matemáticas. Aquí hay una solución para una orientación 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%);
}

Dado que vh cambiará cuando desaparezca la barra de url, debe determinar la altura de otra manera. Afortunadamente, el ancho de la vista es constante y los dispositivos móviles solo vienen en unas pocas relaciones de aspecto diferentes; si puede determinar el ancho y la relación de aspecto, un poco de matemáticas le dará la altura de la vista exactamente como vh debería funcionar. Aquí está el proceso

1) Cree una serie de consultas de medios para las relaciones de aspecto a las que desea dirigirse.

  • Use device-aspect-ratio en lugar de aspect-ratio porque este último cambiará de tamaño cuando la barra url desaparezca

  • Agregué ' max ' a la relación de aspecto del dispositivo para apuntar a cualquier relación de aspecto que suceda entre las más populares. No serán tan precisos, pero serán solo para una minoría de usuarios y seguirán siendo bastante cercanos a los adecuados vh.

  • Recuerde la consulta de medios usando horizontal / vertical, por lo que para portait necesitará voltear los números

2) para cada media query multiplique cualquier porcentaje de altura vertical que desee que el elemento esté en vw por el reverso de la relación de aspecto.

  • Ya que conoce el ancho y la relación de ancho a alto, simplemente multiplique el % que desea (100% en su caso) por la relación de alto/ancho.

3) Tienes que determinar la altura de la barra de url, y luego menos eso de la altura. No he encontrado mediciones exactas, pero uso el 9% para dispositivos móviles en paisaje y eso parece funcionar bastante bien.

Esta no es una solución muy elegante, pero las otras opciones tampoco son muy buenas, teniendo en cuenta que son:

  • Tener su sitio web parece buggy para el usuario,

  • Que tengan elementos de tamaño incorrecto, o

  • Usando javascript para algunos estilos básicos ,

El inconveniente es que algunos dispositivos pueden tener diferentes alturas de barra de url o relaciones de aspecto que los más populares. Sin embargo, usar este método si solo un pequeño número de dispositivos sufren la adición/sustracción de unos pocos píxeles, me parece mucho mejor que todos los que tienen un sitio web redimensionar al deslizar.

Para hacerlo más fácil, también creé un 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%);
  }
}
 14
Author: Jordan Los,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-09 20:50:25

Todas las respuestas aquí están usando window altura, que se ve afectada por la barra de URL. ¿Todos se han olvidado de screen la altura?

Aquí está mi solución 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);
  }
});

Agregar la parte .css() está cambiando el elemento posicionado absoluto inevitablemente alineado arriba a alineado abajo, por lo que no hay salto en absoluto. Aunque, supongo que no hay razón para no añadir directamente a la CSS normal.

Necesitamos el user agent sniffer porque la altura de la pantalla en los escritorios no ser útil.

Además, todo esto asume que #background es un elemento de posición fija que llena la ventana.

Para los puristas de JavaScript (advertencia unt no probado):

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

EDITAR: Lo sentimos, esto no responde directamente a su problema específico con más de un fondo. Pero este es uno de los primeros resultados al buscar este problema de fondos fijos saltando en el móvil.

 11
Author: cr0ybot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-10 01:09:20

También me encontré con este problema cuando estaba tratando de crear una pantalla de entrada que cubriera toda la ventana. Desafortunadamente, la respuesta aceptada ya no funciona.

1) Los elementos con la altura establecida en 100vh se redimensionan cada vez que cambia el tamaño de la ventana, incluidos los casos en que es causado por (des)la barra URL que aparece.

2) $(window).height() devuelve valores también afectados por el tamaño de la barra de URL.

Una solución es "congelar" el elemento usando transition: height 999999s como se sugiere en la respuesta de AlexKempton. La desventaja es que esto deshabilita efectivamente la adaptación a todos los cambios de tamaño de la ventana, incluidos los causados por la rotación de la pantalla.

Así que mi solución es administrar los cambios de viewport manualmente usando JavaScript. Eso me permite ignorar los pequeños cambios que probablemente sean causados por la barra de URL y reaccionar solo en los grandes.

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

EDITAR: En realidad uso esta técnica junto con height: 100vh. La página se representa correctamente desde el mismo comenzando y luego el javascript se inicia y comienza a administrar la altura manualmente. De esta manera no hay parpadeo en absoluto mientras se carga la página (o incluso después).

 9
Author: tobik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-27 20:24:25

La solución que estoy usando actualmente es comprobar el userAgent en $(document).ready para ver si es uno de los navegadores ofensivos. Si lo es, siga estos pasos:

  1. Establezca las alturas relevantes a la altura actual de la ventana en lugar de' 100% '
  2. Almacena el valor horizontal de la ventana actual
  3. Luego, en $(window).resize, solo actualice los valores de altura relevantes si la nueva dimensión horizontal viewport es diferente de su valor inicial
  4. Almacenar la nueva horizontal & valores verticales

Opcionalmente, también puede probar permitir redimensionamientos verticales solo cuando están más allá de la altura de la barra(s) de direcciones.

Oh, y la barra de direcciones afecta a $(window).height. Vea: Cambios en la barra de direcciones del navegador Webkit móvil (chrome) changes(ventana).height(); making background-size:cover reescale every time JS" Window " width-height vs "screen" width-height?

 8
Author: m-p,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:13

Encontré una solución muy fácil sin el uso de Javascript:

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

Todo lo que hace es retrasar el movimiento para que sea increíblemente lento y no se note.

 4
Author: Pav Sidhu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-16 23:01:33

Mi solución implicaba un poco de javascript. Mantenga el 100% o 100vh en el div (esto evitará que el div no aparezca en la carga inicial de la página). Luego, cuando se cargue la página, tome la altura de la ventana y aplíquela al elemento en cuestión. Evita el salto porque ahora tienes una altura estática en tu div.

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

$hero.css('height', h);
 3
Author: Todd Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 20:28:46

He creado una solución javascript vanilla para usar unidades VH. El uso de VH prácticamente en cualquier lugar se efectúa minimizando las barras de direcciones en el desplazamiento. Para arreglar el jank que se muestra cuando la página redibuja, tengo este js aquí que agarrará todos sus elementos usando unidades VH (si les da la clase .vh-fix), y les dará alturas de píxel en línea. Esencialmente congelándolos a la altura que queremos. Puede hacer esto en rotación o en el cambio de tamaño de la ventana para mantenerse receptivo.

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'
  }
}

Esto ha resuelto todos mis casos de uso, espero que ayude.

 3
Author: Geek Devigner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-14 21:55:23

Esta es mi solución para resolver este problema, he añadido comentarios directamente en el código. He probado esta solución y funciona bien. Espero que seamos útiles para todos tiene el mismo problema.

//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
  }); 
};
 2
Author: lino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-23 14:03:55

Esta es la mejor solución (más simple) por encima de todo lo que he intentado.

...¡Y esto no mantiene la experiencia nativa de la barra de direcciones!

Puede establecer la altura con CSS al 100%, por ejemplo, y luego establecer la altura a 0.9 * de la altura de la ventana en px con javascript, cuando se carga el documento.

Por ejemplo con jQuery:

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

)

Desafortunadamente no hay nada que funcione con CSS puro: P, sino que también sea minfull que vh y vw tienen errores en los porcentajes de uso de iPhones.

 2
Author: CelticParser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-11 20:51:18

Establezco mi elemento width, con javascript. Después de poner el 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");

Esto funciona para mí. No uso jquery ect. porque quiero que cargue tan pronto como sea posible.

 1
Author: Johan Hoeksma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-17 06:50:37

Tomó unas horas entender todos los detalles de este problema y averiguar la mejor implementación. Resulta que a partir de abril 2016 solo iOS Chrome tiene este problema. Probé en Android Chrome y iOS Safari both ambos estaban bien sin esta solución. Así que la solución para iOS Chrome que estoy usando es:

$(document).ready(function() {
   var screenHeight = $(window).height();
   $('div-with-background-image').css('height', screenHeight + 'px');
});
 1
Author: littlequest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 16:13:16

Estoy usando esta solución: Fijar la altura de bg1 en la carga de la página por:

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

Luego adjunte un receptor de eventos de redimensionamiento a la ventana que hace esto: si la diferencia de altura después de redimensionar es inferior a 60px (nada más que la altura de la barra de url), entonces no haga nada y si es mayor que 60px, ¡establezca la altura de bg1 nuevamente a la altura de su padre! código completo:

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

PD: Este error es tan irritante!

 0
Author: AadityaTaparia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-12 03:13:36

Similar a @AlexKempton respuesta. (no pude comentar lo siento)

He estado usando largos retrasos de transición para evitar el cambio de tamaño del elemento.

Eg:

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

La compensación con esto es que evita el cambio de tamaño del elemento, incluso en la rotación del dispositivo, sin embargo, podría usar algunos JS para detectar orientationchange y restablecer la altura si esto fuera un problema.

 0
Author: Chris Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-01 03:45:38

Para aquellos que quieran escuchar la altura interior real y el desplazamiento vertical de la ventana mientras el navegador Chrome mobile es la transición de la barra URL de mostrado a oculto y viceversa, la única solución que encontré es establecer una función de intervalo, y medir la discrepancia de la window.innerHeight con su valor anterior.

Esto introduce este código:

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

Espero que esto sea útil. ¿Alguien conoce una solución mejor?

 0
Author: Édouard Mercier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-28 13:26:45

La solución que se me ocurrió al tener un problema similar fue establecer la altura del elemento en window.innerHeight cada vez que se disparaba el evento 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);
    }
});

Esto hace que el elemento abarque exactamente el 100% de la pantalla disponible en todo momento, ni más ni menos. Incluso durante la barra de direcciones ocultar o mostrar.

Sin embargo, es una pena que tengamos que llegar a ese tipo de parches, donde simple position: fixed debería funcionar.

 0
Author: Paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-31 13:32:47