risposta-alla-domanda-sullo-sviluppo-web-bd.com

L'immagine di sfondo salta quando la barra degli indirizzi nasconde iOS/Android/Mobile Chrome

Attualmente sto sviluppando un sito reattivo utilizzando Twitter Bootstrap.

Il sito ha un'immagine di sfondo a schermo intero su dispositivo mobile/tablet/desktop. Queste immagini ruotano e sbiadiscono attraverso ciascuna, usando due div.

È quasi perfetto, tranne un problema. Usando iOS Safari, Android Browser o Chrome su Android lo sfondo salta leggermente quando un utente scorre la pagina verso il basso e fa sì che la barra degli indirizzi si nasconda.

Il sito è qui: http://lt2.daveclarke.me/

Visitalo su un dispositivo mobile e scorri verso il basso e dovresti vedere il ridimensionamento/spostamento dell'immagine. 

Il codice che sto usando per lo sfondo DIV è il seguente:

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

Tutti i suggerimenti sono i benvenuti - questo mi ha fatto venire la testa per un po '!!

157
Dave Clarke

Questo problema è causato dal fatto che le barre degli URL si restringono/scivolano fuori strada e cambiano le dimensioni dei # bg1 e # bg2 div poiché sono al 100% di altezza e "fissi". Poiché l'immagine di sfondo è impostata su "copertina", regola la dimensione/posizione dell'immagine in quanto l'area contenente è più grande.

In base alla natura reattiva del sito, lo sfondo deve essere ridimensionato. Intrattengo due possibili soluzioni:

1) Impostare l'altezza # bg1, # bg2 su 100vh. In teoria, questa è una soluzione elegante. Tuttavia, iOS ha un bug vh ( http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6/ ). Ho tentato di utilizzare un'altezza massima per evitare il problema, ma è rimasto. 

2) Le dimensioni del viewport, quando determinate da Javascript, non sono influenzate dalla barra degli URL. Pertanto, Javascript può essere utilizzato per impostare un'altezza statica su # bg1 e # bg2 in base alla dimensione del viewport. Questa non è la soluzione migliore in quanto non è puro CSS e c'è un leggero salto di immagine sul caricamento della pagina. Tuttavia, è l'unica soluzione praticabile che vedo considerando i bug "vh" di iOS (che non sembrano essere corretti in iOS 7).

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

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

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

Da un lato, ho visto tanti problemi con queste barre di ridimensionamento degli URL in iOS e Android. Capisco lo scopo, ma hanno davvero bisogno di pensare attraverso la strana funzionalità e il caos che portano ai siti web. L'ultima modifica è che non puoi più "nascondere" la barra dell'URL sulla pagina caricata su iOS o Chrome utilizzando i trucchi di scorrimento.

EDIT: Mentre lo script di cui sopra funziona perfettamente per mantenere lo sfondo da ridimensionamento, provoca un gap evidente quando gli utenti scorrere verso il basso. Questo perché mantiene lo sfondo al 100% dell'altezza dello schermo meno la barra dell'URL. Se aggiungiamo 60 px all'altezza, come suggerisce swiss, questo problema scompare. Significa che non vediamo il 60px in basso dell'immagine di sfondo quando la barra dell'URL è presente, ma impedisce agli utenti di vedere sempre una lacuna.

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

Ho scoperto che la risposta di Jason non stava funzionando per me e stavo ancora facendo un salto. Il javascript assicurava che non ci fosse spazio nella parte superiore della pagina, ma lo sfondo stava ancora saltando ogni volta che la barra degli indirizzi scompariva/riappariva. Così come la correzione di Javascript, ho applicato transition: height 999999s al div. Questo crea una transizione con una durata così lunga da bloccare virtualmente l'elemento.

59
AlexKempton

Ho un problema simile su un'intestazione del nostro sito web.

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

Questo finirà in un'esperienza di scorrimento brusco su Android Chrome, perché il .header-container verrà ridimensionato dopo che la barra dell'URL si nasconde e il dito viene rimosso dallo schermo.

Soluzione CSS:

Aggiungendo le due righe seguenti, si impedirà che la barra dell'URL si nasconda e lo scorrimento verticale sia ancora possibile:

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

Il problema può essere risolto con una query multimediale e alcuni calcoli matematici. Ecco una soluzione per un orientamento 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%);
}

Dal momento che vh cambierà quando la barra dell'URL scomparirà, dovrai determinare l'altezza in un altro modo. Per fortuna, la larghezza del viewport è costante e i dispositivi mobili sono disponibili in diversi formati; se riesci a determinare la larghezza e le proporzioni, un po 'di matematica ti darà l'altezza della finestra esattamente come vh dovrebbe funzionare. Ecco il processo

1) Crea una serie di query multimediali per le proporzioni che desideri utilizzare come target.

  • usa il rapporto aspetto del dispositivo invece del rapporto aspetto perché quest'ultimo ridimensiona quando la barra dell'URL scompare

  • Ho aggiunto "max" al rapporto aspetto dispositivo per scegliere come target qualsiasi rapporto di aspetto tra quelli più popolari. Non saranno così precisi, ma saranno solo per una minoranza di utenti e saranno comunque molto vicini al vero vh.

  • ricorda la query multimediale usando orizzontale/verticale, quindi per il portait dovrai capovolgere i numeri

2) per ogni media query moltiplicare qualsiasi percentuale di altezza verticale che si desidera che l'elemento sia in VW dal retro del rapporto di aspetto. 

  • Poiché conosci la larghezza e il rapporto tra larghezza e altezza, devi semplicemente moltiplicare la percentuale che desideri (100% nel tuo caso) in base al rapporto altezza/larghezza. 

3) Devi determinare l'altezza della barra degli url, e quindi meno quella dall'altezza. Non ho trovato misurazioni esatte, ma io uso il 9% per i dispositivi mobili in orizzontale e sembra funzionare piuttosto bene.

Questa non è una soluzione molto elegante, ma anche le altre opzioni non sono molto buone, considerando che sono: 

  • Avere il tuo sito web sembra bug per l'utente, 

  • avere elementi di dimensioni inadeguate, o 

  • Utilizzando javascript per alcuni stili basic

Lo svantaggio è che alcuni dispositivi possono avere altezze di barre degli url o formati diversi rispetto ai più popolari. Tuttavia, usando questo metodo se solo un piccolo numero di dispositivi subisce l'addizione/sottrazione di alcuni pixel, mi sembra molto meglio di chiunque abbia un ridimensionamento del sito web quando si fa scorrere il dito. 

Per semplificare, ho anche creato 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

Tutte le risposte qui utilizzano window height, che è influenzata dalla barra degli URL. Tutti hanno dimenticato l'altezza screen?

Ecco la mia soluzione 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'aggiunta della parte .css() sta modificando l'elemento posizionato in modo assoluto allineato in cima all'allineamento inferiore, quindi non c'è alcun salto. Anche se, suppongo, non c'è ragione di non aggiungerlo direttamente al normale CSS.

Abbiamo bisogno dello sniffer dell'agente utente perché l'altezza dello schermo sui desktop non sarebbe utile.

Inoltre, tutto ciò presuppone che #background sia un elemento a posizione fissa che riempie la finestra.

Per i puristi di JavaScript (avviso - non testato):

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: Mi dispiace che questo non risponda direttamente al tuo problema specifico con più di uno sfondo. Ma questo è uno dei primi risultati durante la ricerca di questo problema di sfondi fissi che saltano sui dispositivi mobili.

12
cr0ybot

Mi sono imbattuto in questo problema anche quando stavo cercando di creare uno schermo di ingresso che avrebbe coperto l'intera finestra. Sfortunatamente, la risposta accettata non funziona più.

1) Gli elementi con l'altezza impostata su 100vh vengono ridimensionati ogni volta che la dimensione del viewport cambia, compresi i casi in cui è causata dalla (dis) visualizzazione della barra degli URL.

2) $(window).height() restituisce valori influenzati anche dalla dimensione della barra degli URL.

Una soluzione è "congelare" l'elemento usando transition: height 999999s come suggerito in la risposta di AlexKempton . Lo svantaggio è che questo disabilita in modo efficace l'adattamento a tutte le modifiche alle dimensioni della vista, comprese quelle causate dalla rotazione dello schermo.

Quindi la mia soluzione è gestire le modifiche delle finestre manualmente usando JavaScript. Ciò mi consente di ignorare le piccole modifiche che potrebbero essere causate dalla barra degli indirizzi e di reagire solo su quelle grandi.

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: effettivamente uso questa tecnica insieme a height: 100vh. La pagina viene renderizzata correttamente fin dall'inizio e quindi il javascript si avvia e inizia a gestire manualmente l'altezza. In questo modo non c'è affatto sfarfallio durante il caricamento della pagina (o anche dopo).

9
tobik

La soluzione che sto attualmente utilizzando è controllare userAgent on $(document).ready per vedere se è uno dei browser offensivi. In tal caso, attenersi alla seguente procedura: 

  1. Imposta le altezze rilevanti sull'altezza attuale della finestra anziché su "100%" 
  2. Memorizza il valore orizzontale della finestra corrente 
  3. Quindi, su $(window).resize, aggiorna solo i valori di altezza rilevanti se la nuova dimensione del viewport horizontal è diversa dal suo valore iniziale 
  4. Memorizza i nuovi valori orizzontali e verticali

Facoltativamente, è anche possibile testare il permesso di ridimensionamento verticale solo quando sono al di sopra dell'altezza delle barre degli indirizzi. 

Oh, e la barra degli indirizzi influenza $(window).height. Vedi: La barra degli indirizzi del browser Webkit per browser Web (chrome) cambia $ (finestra) .height (); fare sfondo: ridimensionare la copertina ogni volta . /. JS "Window" width-height vs "screen" width-height?

8
m-p

Ho trovato una soluzione davvero semplice senza l'uso di Javascript:

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

Tutto ciò fa ritardare il movimento in modo che sia incredibilmente lento che non è evidente.

4
Pav Sidhu

Ho creato una soluzione javascript di Vanilla per utilizzare le unità VH. L'uso di VH praticamente ovunque viene effettuato dalle barre degli indirizzi riducendo al minimo lo scorrimento. Per sistemare la jank che mostra quando la pagina viene ridisegnata, ho qui questo js che catturerà tutti i tuoi elementi usando le unità VH (se gli dai la classe .vh-fix), e fornirai loro pixel inline. Essenzialmente li congelamento all'altezza che vogliamo. Puoi farlo a rotazione o su modifica della dimensione del viewport per rimanere reattivo.

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

Questo ha risolto tutti i miei casi d'uso, spero che aiuti. 

3
Geek Devigner

questa è la mia soluzione per risolvere questo problema, ho aggiunto commenti direttamente sul codice . Ho provato questa soluzione e funziona perfettamente. Spero che saremo utili per tutti ha lo stesso 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
  }); 
};
3
lino

La mia soluzione ha coinvolto un po 'di javascript. Mantenere il 100% o 100vh sul div (questo eviterà che il div non appaia al caricamento iniziale della pagina). Quindi, quando la pagina viene caricata, afferrare l'altezza della finestra e applicarla all'elemento in questione. Evita il salto perché ora hai un'altezza statica sul tuo div.

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

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

Questa è la soluzione migliore (la più semplice) sopra tutto ciò che ho provato.

... E questo non mantiene l'esperienza nativa della barra degli indirizzi !

Ad esempio, puoi impostare l'altezza con CSS al 100%, quindi impostare l'altezza a 0.9 * dell'altezza della finestra in px con javascript, quando il documento viene caricato.

Ad esempio con jQuery:

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

)

Sfortunatamente non c'è nulla che funzioni con CSS puro: P, ma è anche minimo che vh e vw siano buggati sugli iPhone - usa le percentuali. 

2
user742030

Ho impostato il mio elemento di larghezza, con javascript. Dopo aver impostato il 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");

Questo funziona per me. Non uso ject ject. perché lo voglio caricare non appena possibile.

1
Johan Hoeksma

Risposta semplice se lo sfondo lo consente, perché non impostare la dimensione dello sfondo: a qualcosa che copre solo la larghezza del dispositivo con le query multimediali e utilizzare accanto alla posizione: after: fixed; hack qui .

IE: background-size: 901px; per qualsiasi schermo <900px? Non perfetto o reattivo, ma ha funzionato per me su un cellulare <480px mentre sto usando un BG astratto.

1
EasterMedia

Ci sono volute alcune ore per comprendere tutti i dettagli di questo problema e capire la migliore implementazione. A partire da aprile 2016, solo iOS Chrome ha questo problema. Ho provato su Android Chrome e iOS Safari - entrambi erano perfetti senza questa correzione. Quindi la soluzione per iOS Chrome che sto utilizzando è:

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

La soluzione che mi è venuta in mente quando avevo un problema simile era impostare l'altezza dell'elemento su window.innerHeight ogni volta che l'evento touchmove è stato attivato.

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

Questo fa sì che l'elemento si estenda esattamente al 100% dello schermo disponibile in ogni momento, né più né meno. Anche durante la barra degli indirizzi che si nasconde o mostra.

Tuttavia è un peccato che dobbiamo trovare questo tipo di patch, in cui dovrebbe funzionare position: fixed semplice.

0
Paul

Simile alla risposta @AlexKempton. (non posso commentare mi dispiace)

Ho utilizzato lunghi ritardi di transizione per impedire il ridimensionamento degli elementi.

per esempio: 

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

Il compromesso con questo è che impedisce il ridimensionamento dell'elemento anche sulla rotazione del dispositivo, tuttavia, è possibile utilizzare alcuni JS per rilevare orientationchange e ripristinare l'altezza se si tratta di un problema.

0
Chris Anderson

Sto usando questa soluzione: Correggere l'altezza di bg1 al caricamento della pagina:

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

Quindi collega un listener di eventi di ridimensionamento a Window che esegue questo: Se la differenza di altezza dopo il ridimensionamento è inferiore a 60px (qualcosa di più dell'altezza della barra url) allora non fare nulla e se è maggiore di 60px, imposta nuovamente l'altezza di bg1 alla sua altezza del genitore! codice 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;
    }
}

PS: questo bug è così irritante!

0
AadityaTaparia

Per coloro che desiderano ascoltare l'effettiva altezza interna e lo scorrimento verticale della finestra mentre il browser mobile Chrome esegue la transizione, la barra dell'URL viene mostrata in posizione nascosta e viceversa, l'unica soluzione che ho trovato è impostare una funzione intervallo, e misurare la discrepanza di window.innerHeight con il suo valore precedente.

Questo introduce questo codice:

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

Spero che questo sarà utile. Qualcuno conosce una soluzione migliore?

0
Édouard Mercier