Parallasse performante

Paul Lewis
Robert Flack
Robert Flack

Che si tratti di un amore o di un'odio, la parallasse è destinata a durare. Se usata con accortezza, può aggiungono profondità e originalità a un'app web. Il problema, però, è che implementare realizzare una parallasse in modo efficace può essere difficile. In questo articolo, una soluzione che sia efficace e che funzioni cross-browser.

Illustrazione con parallasse.

TL;DR

  • Non utilizzare eventi di scorrimento o background-position per creare animazioni con parallasse.
  • Utilizza le trasformazioni 3D CSS per creare un effetto parallasse più accurato.
  • In Safari per dispositivi mobili, utilizza position: sticky per assicurarti che l'effetto Parallasse vengono propagate.

Se vuoi la soluzione integrata, vai al repository GitHub di esempi di elementi UI e recupera il Assistente Parallasse JS. Puoi vedere una demo dal vivo dello scorrimento parallasse nei GitHub di ASL.

Parallasse di problemi

Per cominciare, diamo un'occhiata a due modi comuni per ottenere una parallasse e, in particolare, perché non sono adatti ai nostri scopi.

Male: vengono utilizzati gli eventi di scorrimento

Il requisito chiave della parallasse è che deve essere accoppiato con scorrimento. della ogni singola modifica nella posizione di scorrimento della pagina, l'elemento di parallasse dovrebbe aggiornarsi. Anche se sembra semplice, un meccanismo importante di browser moderni è la loro capacità di funzionare in modo asincrono. Ciò si applica, nei nostri caso particolare, per scorrere gli eventi. Nella maggior parte dei browser, gli eventi di scorrimento vengono pubblicati come "best effort" e non sono garantiti per essere pubblicati su ogni frame del animazione di scorrimento.

Questa informazione importante ci spiega perché è necessario evitare Soluzione basata su JavaScript che sposta gli elementi in base agli eventi di scorrimento: JavaScript non garantisce che la parallasse mantenga il passo con le la posizione di scorrimento della pagina. Nelle versioni precedenti di Safari per il mobile, gli eventi di scorrimento venivano effettivamente consegnato alla fine del rotolo, il che rendeva impossibile Effetto di scorrimento basato su JavaScript. Le versioni più recenti offrono eventi di scorrimento durante l'animazione, ma, come per Chrome, ha fatto il "best effort" base. Se il thread principale è occupato con qualsiasi altro lavoro, gli eventi di scorrimento non verranno consegnati il che significa che l'effetto Parallasse andrà perso.

Errore: aggiornamento di background-position in corso...

Un'altra situazione che vogliamo evitare è dipingere su ogni fotogramma. Molte soluzioni provare a modificare background-position per ottenere il look parallasse, che fa sì che il browser riproduca le parti interessate della pagina quando scorrono; e che può essere abbastanza costoso da bloccare significativamente l'animazione.

Se vogliamo mantenere la promessa del moto parallasse, vogliamo qualcosa che può essere applicata come proprietà accelerata (che oggi richiede trasformazioni e opacità) e che non si basa su eventi di scorrimento.

CSS in 3D

Sia Scott Kellum che Keith Clark hanno ha svolto un lavoro significativo nell'utilizzo di CSS 3D per ottenere il movimento di parallasse, e la tecnica utilizzata è effettivamente questa:

  • Configura un elemento contenitore per scorrere con overflow-y: scroll (e probabilmente overflow-x: hidden).
  • Allo stesso elemento applica un valore perspective e un perspective-origin impostato su top left o 0 0.
  • Applicare una traslazione in Z agli elementi secondari di quell'elemento e ridimensionarla per fornire un movimento con parallasse senza influenzarne le dimensioni sullo schermo.

Il CSS per questo approccio ha il seguente aspetto:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

Il che presuppone uno snippet di codice HTML simile al seguente:

<div class="container">
    <div class="parallax-child"></div>
</div>

Regolazione della scala per la prospettiva

Se spingi all'indietro l'elemento secondario, questo diventerà più piccolo proporzionale al valore di prospettiva. Puoi calcolare di quanto sarà necessario fare lo scale up con questa equazione: (prospettiva - distanza) / prospettiva. Poiché molto probabilmente vuoi che l'elemento di parallasse risulti in parallasse, ma appaia nelle dimensioni da noi create, dovrebbe essere ampliato in questo modo, piuttosto che essere lasciato così com'è.

Nel caso del codice riportato sopra, la prospettiva è 1px e La distanza Z di parallax-child è -2 px. Ciò significa che l'elemento dovrà lo scale up di 3x, come puoi vedere il valore inserito nel codice: scale(3).

Per tutti i contenuti a cui non è applicato un valore translateZ, puoi: sostituisci un valore pari a zero. Ciò significa che la scala è (prospettiva - 0) / , che calcola il valore 1, il che significa che è stato scalato né verso l'alto né verso il basso. Molto utile, davvero.

Come funziona questo approccio

È importante chiarire perché funziona, poiché la utilizzeremo in poco tempo. Lo scorrimento è in pratica una trasformazione, ed è per questo che può accelerated; comporta per lo più lo spostamento di strati all'interno della GPU. In un scorrimento tipico, ovvero senza alcuna nozione di prospettiva, scorrimento avviene in modo 1:1 quando si confrontano l'elemento di scorrimento e i relativi elementi secondari. Se fai scorrere un elemento verso il basso di 300px, i relativi elementi secondari vengono trasformati verso l'alto. dello stesso importo: 300px.

Tuttavia, l'applicazione di un valore di prospettiva all'elemento scorrevole con questo processo; cambia le matrici che supportano la trasformazione di scorrimento. Ora uno scorrimento di 300 px può spostare gli elementi secondari di 150 px solo, a seconda del perspective e translateZ valori che hai scelto. Se un elemento ha un Se il valore di translateZ è 0, verrà fatto scorrere il dito 1:1 (come in passato), ma un asset secondario spostato in Z lontano dalla prospettiva dell'origine verrà fatto scorrere con un ! Risultato netto: movimento di parallasse. E, cosa molto importante, viene gestito come parte del meccanismo di scorrimento interno del browser, il che significa non è necessario ascoltare gli eventi scroll o modificare background-position.

Una mosca nell'unguento: Safari mobile

Ci sono delle avvertenze per ogni effetto e una delle più importanti per le trasformazioni riguarda di conservare gli effetti 3D degli elementi secondari. Se ci sono elementi in una gerarchia tra l'elemento con una prospettiva e i suoi elementi secondari con parallasse, la prospettiva 3D è "appiattita", nel senso che l'effetto è stato perso.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

Nel codice HTML riportato sopra, il .parallax-container è nuovo e sarà efficace piatte il valore perspective e perdiamo l'effetto parallasse. La soluzione nella maggior parte dei casi è piuttosto semplice: si aggiunge transform-style: preserve-3d all'elemento, causando la propagazione di eventuali effetti 3D (come la nostra ) che sono state applicate più in alto nell'albero.

.parallax-container {
  transform-style: preserve-3d;
}

Nel caso di Safari Mobile, tuttavia, la situazione è un po' più contorta. L'applicazione di overflow-y: scroll all'elemento contenitore funziona tecnicamente, ma in il costo della possibilità di far scorrere l'elemento di scorrimento. La soluzione consiste nell'aggiungere -webkit-overflow-scrolling: touch, ma ridurrà anche perspective senza parallasse.

Da un punto di vista del miglioramento progressivo, probabilmente non si tratta di un problema problema. Se non riusciamo a realizzare il parallasse in ogni situazione, la nostra app continuerà a funzionare, sarebbe utile trovare una soluzione.

position: sticky in soccorso!

Esistono, infatti, alcune fonti di aiuto sotto forma di position: sticky, che esiste per consentire agli elementi di "essere bloccati" nella parte superiore dell'area visibile o a un determinato elemento principale durante lo scorrimento. Le specifiche, come la maggior parte di esse, sono piuttosto pesanti, ma contengono una una piccola gemma utile in:

A prima vista, ciò potrebbe non significare molto, ma un aspetto fondamentale quella frase è quando si riferisce a come, esattamente, la stickiness di un elemento calcolato: "l'offset viene calcolato con riferimento al predecessore più vicino con una casella di scorrimento". In altre parole, la distanza per spostare l'elemento fisso (affinché appaia attaccata a un altro elemento o all'area visibile) è calcolato prima di applicare qualsiasi altra trasformazione, non dopo. Ciò significa come nell'esempio di scorrimento precedente, se l'offset è stato calcolato a 300 px, c'è una nuova opportunità per usare le prospettive (o qualsiasi altra trasformazione) per modificare il valore di offset di 300 px prima che venga applicato a qualsiasi elementi.

Se applichi position: -webkit-sticky all'elemento di parallasse, possiamo "invertire" efficacemente l'effetto di appiattimento di -webkit-overflow-scrolling: touch. Ciò garantisce che l'elemento di parallasse faccia riferimento all'elemento più vicino predecessore con una casella di scorrimento, che in questo caso è .container. Poi, come in precedenza, .parallax-container applica un valore perspective, che modifica l'offset di scorrimento calcolato e crea un effetto parallasse.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

In questo modo viene ripristinato l'effetto Parallasse per Safari per dispositivi mobili, un'ottima notizia per tutti tondo!

Avvertenze sul posizionamento persistente

Tuttavia, c'è una differenza in questo caso: position: sticky modifica il valore le meccaniche della parallasse. Il posizionamento persistente cerca di fissare l'elemento a a scorrimento, mentre una versione non permanente no. Ciò significa che gli elementi parallasse con persistenti sono l'opposto di quello senza:

  • Con position: sticky, più l'elemento è vicino a z=0 meno è che si sposta.
  • Senza position: sticky, più l'elemento è vicino a z=0, più che si sposta.

Se tutto ti sembra un po' astratto, dai un'occhiata a questa demo di Robert Flack, che dimostra come gli elementi si comportano diversamente con e senza persistenti posizionamento. Per vedere la differenza, è necessario Chrome Canary (versione 56) al momento della stesura di questo articolo) o Safari.

Screenshot della prospettiva Parallasse

Una demo di Robert Flack che mostra come position: sticky influisce sullo scorrimento con parallasse.

Diversi bug e soluzioni alternative

Come per ogni cosa, però, ci sono ancora protuberanze e protuberanze che devono attenuato su:

  • Il supporto fisso non è coerente. L'assistenza è ancora in fase di implementazione in Chrome, Edge non sono completamente supportati e Firefox presenta bug di pittura quando gli annunci persistenti vengono combinati con le trasformazioni di prospettive. In tale casi, vale la pena inserire un piccolo codice per aggiungere solo position: sticky (il versione con prefisso -webkit-) quando è necessario (per Safari per dispositivi mobili) .
  • L'effetto non "funziona solo" in Edge. Edge prova a gestire lo scorrimento verso a livello di sistema operativo, il che è generalmente un aspetto positivo, ma in questo caso dal rilevare i cambiamenti di prospettiva durante lo scorrimento. Per risolvere il problema, puoi aggiungere un elemento con posizione fissa, poiché sembra passare da Edge a un metodo di scorrimento non del sistema operativo, e assicura che tenga conto dei cambiamenti di prospettiva.
  • "I contenuti della pagina sono diventati davvero vasti!" Molti browser tengono conto della scala quando decidono le dimensioni dei contenuti della pagina, ma purtroppo Chrome e Safari non tengono conto del punto di vista. Quindi... se a un elemento è stata applicata una scala pari a 3 volte, le barre di scorrimento e simili, anche se l'elemento si trova a 1x dopo il perspective è stato applicato. È possibile aggirare il problema ridimensiona gli elementi dall'angolo in basso a destra (con transform-origin: bottom right), perché fa crescere gli elementi di grandi dimensioni nell'elemento "regione esclusa" (in genere in alto a sinistra) dell'area scorrevole; scorrevole le regioni non ti consentono mai di visualizzare o scorrere verso i contenuti della regione esclusa.

Conclusione

Il parallasse è un effetto divertente se usato in modo intelligente. Come puoi vedere, è possibile per implementarlo in un modo performante, accoppiato con lo scorrimento e cross-browser. Poiché richiede un po' di agitazione matematica e una piccola boilerplate per ottenere l'effetto desiderato, abbiamo creato una piccola raccolta di supporto e un esempio, che puoi trovare nel nostro repository GitHub di esempi di elementi UI.

Gioca e facci sapere cosa ne pensi.