L'image de fond saute lorsque la barre d'adresse cache iOS / Android / mobile Chrome

je suis en train de développer un site responsive en utilisant Twitter Bootstrap.

le site a une image d'arrière-plan plein écran à travers mobile/tablette/bureau. Ces images tournent et s'estompent à travers chacune, en utilisant deux divs.

C'est presque parfait, sauf un problème. En utilisant iOS Safari, Android Browser ou Chrome sur Android, l'arrière-plan saute légèrement lorsqu'un utilisateur défile la page et fait masquer la barre d'adresse.

le le site est ici: http://lt2.daveclarke.me /

Visite sur un appareil mobile et faites défiler vers le bas et vous devriez voir l'image redimensionner/déplacer.

le code que j'utilise pour la div background est le suivant:

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

toutes les suggestions sont les bienvenues - Cela fait un moment que j'y pense!!

153
demandé sur Dave Clarke 2014-07-25 02:14:53

18 réponses

ce problème est causé par les barres D'URL rétrécissant/glissant hors du chemin et changeant la taille des divs #bg1 et #bg2 car ils sont 100% hauteur et"fixe". Puisque l'image de fond est définie à "cover", elle ajustera la taille/position de l'image puisque la zone contenant est plus grande.

selon la nature réactive du site, le fond doit être à l'échelle. J'envisage deux solutions possibles:

1) Réglez la hauteur #bg1, #bg2 à 100vh. Dans théorie, c'est une solution élégante. Cependant, iOS a un VH bug ( http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6 / ). J'ai essayé d'utiliser un max-height pour prévenir le problème, mais il est resté.

2) La Taille du viewport, déterminée par Javascript, n'est pas affectée par la barre D'URL. Par conséquent, Javascript peut être utilisé pour définir une hauteur statique sur les #bg1 et #bg2 en fonction de la taille de viewport. Ce n'est pas la meilleure solution n'est pas pur CSS et il y a un léger saut d'image au chargement de la page. Cependant, c'est la seule solution viable que je vois en considérant les bugs "vh" de iOS (qui ne semblent pas être corrigés dans iOS 7).

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

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

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

sur une note secondaire, j'ai vu tellement de problèmes avec ces barres d'URL de redimensionnement dans iOS et Android. Je comprends le but, mais ils ont vraiment besoin de réfléchir à travers les fonctionnalités étranges et les ravages qu'ils apportent aux sites web. Le dernier changement, est que vous ne pouvez plus "cacher" la barre D'URL sur chargement de la page sur iOS ou Chrome en utilisant des trucs de défilement.

EDIT: alors que le script ci-dessus fonctionne parfaitement pour empêcher le redimensionnement de l'arrière-plan, il provoque un écart notable lorsque les utilisateurs font défiler vers le bas. C'est parce qu'il garde l'arrière-plan dimensionné à 100% de la hauteur de l'écran moins la barre D'URL. Si nous ajoutons 60px à la hauteur, comme le suggère swiss, ce problème disparaît. Cela signifie que nous ne pouvons pas voir le 60px inférieur de l'image de fond lorsque la barre D'URL est présente, mais il empêche les utilisateurs de voir un trou.

function resizeBackground() {
    bg.height( $(window).height() + 60);
}
95
répondu Jason 2017-01-11 12:14:31

J'ai trouvé que la réponse de Jason ne marchait pas tout à fait pour moi et j'étais encore en train de faire un saut. Le Javascript assurait qu'il n'y avait pas de vide en haut de la page, mais le fond sautait toujours à chaque fois que la barre d'adresse disparaissait/réapparaissait. Ainsi, en plus du correctif Javascript, j'ai appliqué transition: height 999999s au div. Cela crée une transition avec une durée si longue qu'elle gèle pratiquement l'élément.

58
répondu AlexKempton 2015-01-26 13:31:29

j'ai un problème similaire sur un en-tête de notre site web.

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

cela finira dans une expérience de défilement sur android chrome, parce que le .header-container va resquiller après la barre d'url se cache et le doigt est retiré de l'écran.

CSS-Solution:

L'ajout des deux lignes suivantes empêchera que la barre d'url se cache et que le défilement vertical soit encore possible:

html {
    overflow: hidden;
}
body {
    overflow-y: scroll;
    -webkit-overflow-scrolling:touch;
}
21
répondu mat 2015-10-31 18:56:36

le problème peut être résolu avec une requête média et quelques mathématiques. Voici une solution pour une orientation portante:

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

puisque vh va changer quand la barre d'url se dissout, vous devez déterminer la hauteur d'une autre manière. Heureusement, la largeur du viewport est constante et les appareils mobiles ne viennent que dans quelques rapports d'aspect différents; si vous pouvez déterminer la largeur et le rapport d'aspect, un peu de mathématiques vous donnera la hauteur du viewport exactement comme vh devrait marcher. Voici le processus

1) créez une série de requêtes Médias pour les rapports d'aspect que vous voulez cibler.

  • utilisez device-aspect-ratio au lieu de l'aspect-ratio parce que ce dernier se redimensionnera lorsque la barre d'url disparaîtra

  • j'ai ajouté " max " au rapport d'aspect de l'appareil pour cibler n'importe quel rapport d'aspect qui arrive à suivre entre les plus populaires. Ils ne seront pas aussi précis, mais ils seront seulement pour une minorité d'utilisateurs et sera toujours assez proche de la bonne vh.

  • souvenez-vous de la requête médias en utilisant horizontal / vertical , donc pour le portail, vous aurez besoin de retourner les numéros

2) pour chaque requête média, multipliez le pourcentage de hauteur verticale que vous voulez que l'élément soit en vw par l'inverse du rapport d'aspect.

  • Puisque vous savez que la largeur et le rapport de la largeur à la hauteur, vous multipliez simplement le % que vous voulez (100% dans votre cas) par le ratio hauteur/largeur.

3) vous devez déterminer la hauteur de la barre d'url, puis moins que de la hauteur. Je n'ai pas trouvé de mesures exactes, mais j'utilise 9% pour les appareils mobiles dans landscape et cela semble fonctionner assez bien.

ce n'est pas une solution très élégante, mais les autres options ne sont pas très bonnes non plus, considérant qu'ils sont:

  • avoir votre site Web semble bogué à l'utilisateur,

  • comportant des éléments mal dimensionnés, ou

  • utilisant javascript pour certains basic styling,

l'inconvénient est que certains appareils peuvent avoir des hauteurs de barre d'url ou des rapports d'aspect différents que les plus populaires. Cependant, en utilisant cette méthode si seulement un petit nombre d'appareils souffrent de l'addition/soustraction de quelques pixels, cela me semble beaucoup mieux que tout le monde ayant un site redimensionner lors du balayage.

pour le rendre plus facile, j'ai aussi créé 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
répondu Jordan Los 2015-06-09 20:50:25

toutes les réponses ici sont en utilisant window hauteur, qui est affectée par la barre D'URL. Tout le monde a oublié la hauteur de screen ?

voici ma solution 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);
  }
});

L'ajout de la partie .css() change inévitablement l'élément de positionnement absolu aligné du haut vers le bas, de sorte qu'il n'y a pas de saut du tout. Bien que, je suppose qu'il n'y a aucune raison de ne pas ajouter ça directement à la CSS normale.

nous avons besoin de l'agent utilisateur sniffer parce que la hauteur de l'écran sur les ordinateurs de bureau ne serait pas utile.

aussi, tout cela suppose que #background est un élément fixe qui remplit la fenêtre.

pour les puristes JavaScript (avertissement--non testé):

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: Désolé de ne pas répondre directement à votre problème spécifique avec plus d'un arrière-plan. Mais c'est l'un des premiers résultats de la recherche problème des fonds fixes sautant sur mobile.

11
répondu cr0ybot 2015-07-10 01:09:20

j'ai rencontré ce problème aussi bien quand j'ai essayé de créer un écran d'entrée qui couvrirait l'ensemble du viewport. Malheureusement, la réponse acceptée ne fonctionne plus.

1)les éléments avec la hauteur définie à 100vh sont redimensionnés chaque fois que la taille du viewport change, y compris les cas où elle est causée par la barre D'URL (dis) apparaissant.

2) $(window).height() renvoie des valeurs également affectées par la taille de la barre D'URL.

une solution consiste à" geler "l'élément en utilisant transition: height 999999s comme suggéré dans la réponse d'AlexKempton . L'inconvénient est que cela empêche effectivement l'adaptation à tous les changements de taille de viewport, y compris ceux causés par la rotation de l'écran.

donc ma solution est de gérer manuellement les modifications de viewport en utilisant JavaScript. Cela me permet d'ignorer les petits changements qui sont susceptibles d'être causées par la barre d'URL et ne réagissent que sur les gros.

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: j'utilise en fait cette technique avec height: 100vh . La page est rendue correctement dès le début, puis le javascript s'active et commence à gérer la hauteur manuellement. De cette façon, il n'y a aucun clignotement pendant le chargement de la page (ou même après).

9
répondu tobik 2018-08-27 20:24:25

la solution que j'utilise actuellement est de vérifier le userAgent sur $(document).ready pour voir si c'est l'un des navigateurs incriminés. Si c'est le cas, suivez les étapes suivantes:

  1. régler les hauteurs pertinentes à la hauteur actuelle du viewport plutôt que "100%
  2. stocker la valeur horizontale actuelle du viewport
  3. puis , sur $(window).resize , mettre à jour les valeurs de hauteur pertinentes seulement si le nouveau horizontal la dimension de viewport est différente de sa valeur initiale
  4. stocker les nouvelles valeurs horizontales et verticales

en option, vous pouvez également tester l'autorisation de redimensionnement vertical seulement quand ils sont au-delà de la hauteur de la barre d'adresse(s).

Oh, et la barre d'adresse affecte $(window).height . Voir: navigateur Webkit Mobile (chrome) changements d'adresse $(fenêtre).hauteur(); faire de background-size:cover redimensionner chaque fois JS "Fenêtre" de largeur-hauteur vs "écran" de largeur-hauteur?

8
répondu m-p 2017-05-23 11:47:13

j'ai trouvé une solution vraiment facile sans L'utilisation de Javascript:

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

Tout cela ne fait que retarder le mouvement de sorte qu'il est incroyablement lent qu'il n'est pas perceptible.

4
répondu Pav Sidhu 2016-12-16 23:01:33

ma solution impliquait un peu de javascript. Gardez le 100% ou 100vh sur le div (cela évitera que le div n'apparaisse pas sur la page de charge initiale). Ensuite, lorsque la page se charge, saisissez la hauteur de la fenêtre et appliquez-la à l'élément en question. Évite le saut parce que maintenant vous avez une hauteur statique sur votre div.

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

$hero.css('height', h);
3
répondu Todd Miller 2015-05-12 20:28:46

j'ai créé une solution JavaScript vanille pour l'utilisation des unités VH. L'utilisation de VH à peu près n'importe où est effectuée par des barres d'adresse minimisant sur le rouleau. Pour corriger le jank qui montre quand la page se redessine, j'ai ce js ici qui va saisir tous vos éléments en utilisant des unités VH (si vous leur donnez la classe .vh-fix ), et leur donner des hauteurs de pixel inlined. On les congèle à la hauteur qu'on veut. Vous pouvez faire cela sur la rotation ou sur le changement de taille de viewport pour rester réactif.

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

cela a résolu tous mes cas d'utilisation, espérons que cela aide.

3
répondu Geek Devigner 2016-02-14 21:55:23

c'est ma solution pour résoudre ce problème, j'ai ajouté des commentaires directement sur le code. J'ai testé cette solution et ça marche très bien. Espérons que nous seront utiles pour tout le monde a le même problème.

//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
répondu lino 2016-02-23 14:03:55

C'est la meilleure solution (la plus simple) au-dessus de tout ce que j'ai essayé.

...Et cela n'empêche pas l'expérience native de la barre d'adresse !

vous pouvez régler la hauteur avec CSS à 100% par exemple, puis la hauteur à 0.9 * de la hauteur de la fenêtre en px avec javascript, lorsque le document est chargé.

par exemple avec jQuery:

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

)

malheureusement, il n'y a rien qui fonctionne avec CSS pur :P mais aussi être minfull que vh et vw sont buggy sur iPhones - pourcentages d'utilisation.

2
répondu CelticParser 2016-04-11 20:51:18

j'ai paramétré mon élément width, avec javascript. Après avoir réglé le 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");

ça me va. Je n'utilise pas jquery ect. parce que je veux qu'il se charge dès que possible.

1
répondu Johan Hoeksma 2015-06-17 06:50:37

Il a fallu quelques heures pour comprendre tous les détails de ce problème et trouver la meilleure application. Il s'avère qu'en avril 2016 seulement iOS Chrome a ce problème. J'ai essayé sur Android Chrome et iOS Safari-- les deux allaient bien sans cette correction. Donc le correctif pour iOS Chrome que j'utilise est:

$(document).ready(function() {
   var screenHeight = $(window).height();
   $('div-with-background-image').css('height', screenHeight + 'px');
});
1
répondu littlequest 2016-04-28 16:13:16

j'utilise cette solution: Fixer la hauteur de bg1 sur la page charge par:

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

puis attachez un écouteur d'événement redimensionné à la fenêtre qui fait ceci: si la différence de hauteur après le redimensionnement est inférieure à 60px (n'importe quoi de plus que la hauteur de la barre d'url) alors ne faites rien et si elle est supérieure à 60px alors réglez la hauteur de bg1 de nouveau à la hauteur de ses parents! code complet:

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: ce bug est tellement irritant!

0
répondu AadityaTaparia 2016-09-12 03:13:36

semblable à la réponse de @AlexKempton. (ne pouvait pas commenter désolé)

j'ai utilisé de longs délais de transition pour empêcher le redimensionnement de l'élément.

par exemple:

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

le compromis avec ceci est il empêche le redimensionnement de l'élément y compris sur le dispositif tournant, cependant, vous pourriez utiliser quelques JS pour détecter orientationchange et réinitialiser la hauteur si cela était un problème.

0
répondu Chris Anderson 2017-05-01 03:45:38

pour ceux qui voudraient écouter la hauteur intérieure réelle et le défilement vertical de la fenêtre alors que le navigateur mobile Chrome est la transition de la barre D'URL montré à caché et vice versa, la seule solution que j'ai trouvé est de définir une fonction d'intervalle, et de mesurer la divergence du window.innerHeight avec sa valeur précédente.

introduit ce code:

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

j'espère que ce sera maniable. Personne ne sait d'une meilleure solution?

0
répondu Édouard Mercier 2017-08-28 13:26:45

la solution que j'ai trouvée en ayant un problème similaire était de régler la hauteur de l'élément à window.innerHeight chaque fois que touchmove événement a été tiré.

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

cela rend l'envergure de l'élément exactement 100% de l'écran disponible à tout moment, ni plus ni moins. Même pendant que la barre d'adresse se cache ou se montre.

néanmoins il est dommage que nous ayons à trouver ce genre de patches, où simple position: fixed devrait fonctionner.

0
répondu Paul 2017-08-31 13:32:47