web-dev-qa-db-fra.com

L'image d'arrière-plan saute lorsque la barre d'adresse masque iOS/Android/Mobile Chrome

Je développe actuellement un site responsive utilisant Twitter Bootstrap.

Le site a une image d’arrière-plan en plein écran sur mobile/tablette/ordinateur de bureau. Ces images tournent et disparaissent à l’aide de deux divs.

C'est presque parfait, sauf un problème. Avec iOS Safari, le navigateur Android ou Chrome sous Android, l'arrière-plan saute légèrement lorsqu'un utilisateur fait défiler la page et provoque le masquage de la barre d'adresse.

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

Visitez-le 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 d'arrière-plan 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 me trotte dans la tête depuis un moment !!

157
Dave Clarke

Ce problème est dû au fait que les barres d'URL se rétrécissent ou disparaissent et que vous modifiez la taille des div # bg1 et # bg2 car elles ont une hauteur de 100% et sont "fixes". Étant donné que l’image d’arrière-plan est réglée sur "Couverture", la taille/position de l’image sera ajustée à mesure que la zone de confinement est plus grande.

En fonction de la réactivité du site, l’arrière-plan doit être redimensionné. Je divertis deux solutions possibles:

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

2) La taille de la fenêtre d'affichage, lorsqu'elle est 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 # bg1 et # bg2 en fonction de la taille de la fenêtre. Ce n'est pas la meilleure solution car ce n'est pas du pur CSS et il y a un léger saut d'image lors du chargement de la page. Cependant, c’est la seule solution viable que je vois compte tenu des bogues «vh» d’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();

En passant, j'ai constaté tant de problèmes avec le redimensionnement de ces barres d'URL sous iOS et Android. Je comprends l'objectif, mais ils doivent vraiment réfléchir aux fonctionnalités étranges et aux ravages qu'ils apportent aux sites Web. La dernière modification est que vous ne pouvez plus "masquer" la barre d’URL lors du chargement de page sur iOS ou Chrome à l’aide d’astuces de défilement.

EDIT: Bien que le script ci-dessus fonctionne parfaitement pour empêcher le redimensionnement de l’arrière-plan, il crée un écart important lorsque les utilisateurs font défiler la liste. En effet, la taille de l'arrière-plan est réduite à 100% de la hauteur de l'écran moins la barre d'URL. Si nous ajoutons 60px à la hauteur, comme le suggère swiss, le problème disparaît. Cela signifie que nous ne parvenons pas à voir les 60 pixels de fond de l'image d'arrière-plan lorsque la barre d'URL est présente, mais cela empêche les utilisateurs de voir un écart.

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

J'ai trouvé que la réponse de Jason ne fonctionnait pas vraiment pour moi et que je continuais à faire un saut. Le Javascript garantissait qu’il n’y avait pas d’espace en haut de la page, mais l’arrière-plan sautait toujours chaque fois que la barre d’adresse disparaissait/réapparaissait. Donc, en plus du correctif Javascript, j'ai appliqué transition: height 999999s à la div. Cela crée une transition d’une durée si longue qu’il fige pratiquement l’élément.

59
AlexKempton

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

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

Cela se traduira par une expérience de défilement nerveuse sur Android Chrome, car le conteneur .header sera redimensionné une fois que la barre d'URL se sera masquée et que le doigt aura été retiré de l'écran.

CSS-Solution:

L'ajout des deux lignes suivantes empêchera que la barre d'URL se cache et le défilement vertical est toujours possible:

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

Le problème peut être résolu avec une requête multimédia et quelques calculs. Voici une solution pour une orientation portrait:

@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 changera lorsque la barre d’URL disparaîtra, vous devez déterminer la hauteur d’une autre manière. Heureusement, la largeur de la fenêtre d'affichage est constante et les appareils mobiles ne présentent que quelques rapports d'aspect différents. Si vous pouvez déterminer la largeur et le rapport hauteur/largeur, un peu de mathématique vous donnera la hauteur de la fenêtre d'affichage exactement comme vh should work. Voici le processus

1) Créez une série de requêtes multimédia pour les proportions que vous souhaitez cibler.

  • utilisez le format de l'appareil au lieu du format car ce dernier sera redimensionné lorsque la barre d'URL disparaît

  • J'ai ajouté «max» au format de l'appareil pour cibler tous les rapports d'aspect qui suivent entre les plus populaires. Ils ne seront pas aussi précis, mais ils ne le seront que pour une minorité d’utilisateurs et seront tout de même assez proches du bon vh.

  • souvenez-vous de la requête de média en utilisant horizontal/vertical, donc pour portrait, vous devez inverser les chiffres

2) pour chaque requête multimédia, multipliez le pourcentage de hauteur verticale souhaité pour l'élément par vw par l'inverse du rapport de format. 

  • Puisque vous connaissez la largeur et le rapport largeur/hauteur, il vous suffit de multiplier le% souhaité (100% dans votre cas) par le rapport hauteur/largeur. 

3) Vous devez déterminer la hauteur de la barre d’URL, puis moins à partir de la hauteur. Je n'ai pas trouvé de mesures exactes, mais j'utilise 9% pour les appareils mobiles en mode paysage 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, vu qu'elles sont: 

  • Avoir votre site web semble bogué à l'utilisateur, 

  • ayant des éléments de taille incorrecte, ou 

  • Utiliser javascript pour certains basic styling, 

L’inconvénient est que certains appareils peuvent avoir des hauteurs de barre d’URL ou des rapports de format différents de ceux utilisés par les plus populaires. Cependant, si cette méthode est utilisée si un petit nombre d’appareils subit l’addition/soustraction de quelques pixels, il me semble que cela va beaucoup mieux que tout le monde qui redimensionne son site Web en balayant. 

Pour faciliter les choses, j'ai également créé un mixin 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%);
  }
}
14
Jordan Los

Toutes les réponses ici utilisent window height, qui est affecté par la barre d’URL. Est-ce que tout le monde a oublié la hauteur 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() modifie le positionnement absolu inévitablement de l'élément positionné en haut, en position alignée en bas, de sorte qu'il n'y a pas de saut du tout. Bien que, je suppose, il n’ya aucune raison de ne pas ajouter cela directement au CSS normal.

Nous avons besoin du renifleur d’agent d’utilisateur, car la hauteur de l’écran sur les ordinateurs de bureau ne serait pas utile.

En outre, tout cela est supposé en supposant que #background est un élément à position fixe remplissant 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é que cela ne réponde pas 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 de ce problème de fond fixe sautant sur mobile.

12
cr0ybot

J'ai rencontré ce problème également lorsque j'essayais de créer un écran d'entrée couvrant toute la fenêtre. Malheureusement, la réponse acceptée ne fonctionne plus.

1) Les éléments dont la hauteur est définie sur 100vh sont redimensionnés chaque fois que la taille de la fenêtre d'affichage change, y compris dans les cas où cela est causé par l'apparition (ou la suppression) de la barre d'adresse.

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 de AlexKempton . L'inconvénient est que cela désactive efficacement l'adaptation à tous les changements de taille de la fenêtre d'affichage, y compris ceux causés par la rotation de l'écran.

Ma solution consiste donc à gérer les modifications de la fenêtre d'affichage manuellement à l'aide de JavaScript. Cela me permet d’ignorer les petits changements susceptibles d’être causés par la barre d’URL et de ne réagir que sur les plus 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 cette technique avec height: 100vh. La page est rendue correctement depuis le début, puis le javascript entre en action et commence à gérer la hauteur manuellement. De cette façon, il n'y a pas de scintillement du tout pendant le chargement de la page (ou même après).

9
tobik

La solution que j'utilise actuellement est de vérifier la userAgent sur $(document).ready pour voir si c'est l'un des navigateurs incriminés. Si c'est le cas, procédez comme suit: 

  1. Définissez les hauteurs appropriées sur la hauteur actuelle de la fenêtre d'affichage plutôt que sur «100%». 
  2. Stocker la valeur horizontale de la fenêtre courante 
  3. Ensuite, sur $(window).resize, ne mettez à jour les valeurs de hauteur appropriées que si la nouvelle dimension de la fenêtre horizontale est différente de sa valeur initiale. 
  4. Stocker les nouvelles valeurs horizontales et verticales

Facultativement, vous pouvez également tester l’autorisation des redimensionnements verticaux uniquement lorsque leur hauteur dépasse la hauteur de la ou des barres d’adresse. 

Oh, et la barre d'adresse affecte $(window).height. Voir: Modifications de la barre d'adresse du navigateur Mobile Webkit (chrome) $ (window) .height (); making background-size: couverture à chaque foisJS "Fenêtre" largeur-hauteur vs "écran" largeur-hauteur?

8
m-p

J'ai trouvé une solution vraiment simple sans l'utilisation de Javascript:

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

Cela ne fait que retarder le mouvement de sorte qu'il soit incroyablement lent et invisible.

4
Pav Sidhu

J'ai créé une solution javascript Vanilla pour utiliser des unités VH. L'utilisation de VH à peu près n'importe où est effectuée par des barres d'adresse minimisant le défilement. Pour réparer le jank qui montre quand la page 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 pixels alignées. Les congeler à la hauteur que nous voulons. Vous pouvez le faire en effectuant une rotation ou un changement de taille de fenêtre 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 aidera. 

3
Geek Devigner

c’est ma solution pour résoudre ce problème, j’ai ajouté des commentaires directement sur le code ..___. J'espère que nous serons utiles car 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
  }); 
};
3
lino

Ma solution impliquait un peu de javascript. Conservez 100% ou 100vh sur la div (cela évitera que la div ne figure pas au chargement initial de la page). Lorsque la page est chargée, saisissez la hauteur de la fenêtre et appliquez-la à l'élément en question. Evite les sauts car vous avez maintenant une hauteur statique sur votre div.

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

$hero.css('height', h);
3
Todd Miller

Je règle 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");

Cela fonctionne pour moi. Je n'utilise pas jquery ect. parce que je veux le charger dès que possible.

1
Johan Hoeksma

Réponse simple si votre arrière-plan le permet, pourquoi ne pas définir la taille de l'arrière-plan: quelque chose qui couvre uniquement la largeur du périphérique avec des requêtes de média et qui est utilisé à côté de la position: après: fixed bidouille ici .

IE: taille de l'arrière-plan: 901px; pour tous les écrans <900px? Pas parfait ou réactif, mais a fonctionné un charme pour moi sur mobile <480px comme je suis en utilisant un BG abstrait.

1
EasterMedia

Il a fallu quelques heures pour comprendre tous les détails de ce problème et déterminer la meilleure mise en œuvre. En avril 2016, seul iOS Chrome avait ce problème. J'ai essayé sur Android Chrome et iOS Safari - les deux allaient bien sans ce correctif. Donc, le correctif pour iOS Chrome que j'utilise est le suivant:

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

La solution que j'ai trouvée avec un problème similaire était de régler la hauteur de l'élément sur window.innerHeight chaque fois que l'événement touchmove était déclenché.

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 fait que l'élément couvre exactement 100% de l'écran disponible à tout moment, ni plus ni moins. Même pendant la barre d'adresse cacher ou montrer.

Néanmoins, il est dommage que nous devions proposer ce type de correctifs, où le simple position: fixed devrait fonctionner.

0
Paul

Similaire à la réponse @AlexKempton. (je n'ai pas pu 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 qu'il empêche le redimensionnement de l'élément, y compris lors de la rotation du périphérique. Cependant, vous pouvez utiliser certains JS pour détecter orientationchange et réinitialiser la hauteur si cela posait problème.

0
Chris Anderson

J'utilise cette solution de contournement: Fixer la hauteur de bg1 au chargement de la page par:

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

Ensuite, attachez un écouteur d’événement resize à la fenêtre, ce qui fait ceci: la taille des 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
AadityaTaparia

Pour ceux qui souhaitent écouter la hauteur intérieure réelle et le défilement vertical de la fenêtre alors que le navigateur Web mobile Chrome effectue la transition de la barre d’URL à la valeur masquée et inversement, la seule solution que j’ai trouvée consiste à définir une fonction d’intervalle mesurer l'écart entre le window.innerHeight et sa valeur précédente.

Ceci 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 cela vous sera utile. Est-ce que quelqu'un connaît une meilleure solution?

0
Édouard Mercier