Não se engane: o paralaxe chegou para ficar. Quando usado criteriosamente, pode adicionar profundidade e sutileza a um aplicativo da web. No entanto, o problema é que a implementação fazer paralaxe com bom desempenho pode ser desafiador. Neste artigo, vamos falar sobre discutir uma solução com bom desempenho e que funcione entre navegadores.
Texto longo, leia o resumo
- Não use eventos de rolagem ou
background-position
para criar animações de paralaxe. - Use as transformações CSS 3D para criar um efeito de paralaxe mais preciso.
- Para o Mobile Safari, use
position: sticky
para garantir que o efeito paralaxe sejam propagadas.
Se você quiser a solução drop-in, acesse o repositório do GitHub para exemplos de elementos da interface e faça o seguinte: JavaScript auxiliar de paralaxe! Veja uma demonstração ao vivo do botão de rolagem de paralaxe na repositório do GitHub.
Paralaxantes que apresentam problemas
Para começar, daremos uma olhada em duas formas comuns de obter uma paralaxe efeito e, em especial, por que elas são inadequadas para nossos objetivos.
Ruim: uso de eventos de rolagem
O principal requisito do paralaxe é que ele seja acoplado à rolagem. para cada mudança na posição de rolagem da página, o valor do elemento de paralaxe deve ser atualizada. Embora isso pareça simples, um mecanismo importante de dos navegadores mais recentes é a capacidade de trabalhar de maneira assíncrona. Isso se aplica, em nossa para eventos de rolagem. Na maioria dos navegadores, os eventos de rolagem são exibidos como “melhor esforço” e não há garantia de exibição em todos os frames da animação de rolagem.
Essa informação importante nos diz por que precisamos evitar uma Solução baseada em JavaScript que move elementos de acordo com eventos de rolagem: O JavaScript não garante que o paralaxe esteja alinhado com a a posição de rolagem da página. Em versões mais antigas do Mobile Safari, os eventos de rolagem eram entregue no final da rolagem, o que tornava impossível fazer uma Efeito de rolagem baseado em JavaScript. As versões mais recentes oferecem eventos de rolagem durante a animação, mas, assim como no Chrome, com base no "melhor esforço", base. Se o A linha de execução principal está ocupada com qualquer outro trabalho, os eventos de rolagem não serão entregues. imediatamente, o que significa que o efeito paralaxe será perdido.
Ruim: atualizando background-position
Outra situação que gostaríamos de evitar é pintar em todos os frames. Muitas soluções
tentar mudar background-position
para fornecer a aparência de paralaxe, que
faz com que o navegador repinte as partes afetadas da página ao rolar, e que
pode ser caro o suficiente para atrapalhar significativamente a animação.
Se quisermos cumprir a promessa do movimento de paralaxe, queremos algo que pode ser aplicada como uma propriedade acelerada (o que hoje significa manter transformações e opacidade) e que não depende de eventos de rolagem.
CSS em 3D
Tanto Scott Kellum quanto Keith Clark têm fez um trabalho significativo na área de uso de CSS 3D para alcançar o movimento de paralaxe, e a técnica usada é, na prática, esta:
- Configure um elemento contêiner para rolar com a
overflow-y: scroll
(e provavelmenteoverflow-x: hidden
). - Para esse mesmo elemento, aplique um valor
perspective
e umperspective-origin
Defina comotop left
ou0 0
. - Aos filhos desse elemento, aplique uma tradução em Z e escalone-os de volta para oferecer movimento de paralaxe sem afetar o tamanho na tela.
O CSS para essa abordagem tem a seguinte aparência:
.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);
}
Ele pressupõe um snippet de HTML como este:
<div class="container">
<div class="parallax-child"></div>
</div>
Ajustando a escala da perspectiva
Enviar o elemento filho de volta fará com que ele fique menor, proporcionalmente ao valor de perspectiva. Você pode calcular quanto precisará ser escalonado verticalmente esta equação: (perspective - distância) / perspectiva. Como provavelmente quer que o elemento paralaxe seja paralaxe, mas apareça no tamanho que o criamos, ele precisaria ser escalonado verticalmente, em vez de permanecer como está.
No caso do código acima, a perspectiva é 1px, e a
A distância Z de parallax-child
é -2 px. Isso significa que o elemento precisará
ser escalonado verticalmente em 3x, que é o valor conectado ao código:
scale(3)
Para qualquer conteúdo que não tenha um valor de translateZ
aplicado, você pode
substitua um valor de zero. Isso significa que a escala é (perspective - 0) /
perspectiva, que resulta em um valor de 1, o que significa que ele foi dimensionado
nem para cima nem para baixo. Isso é muito útil.
Como essa abordagem funciona
É importante esclarecer por que isso funciona, já que vamos usá-lo
conhecimento logo. A rolagem é efetivamente uma transformação e, por isso, ela pode ser
accelerated; envolve principalmente a troca de camadas com a GPU. Em um
de rolagem típica, que é aquela sem noção de perspectiva, a rolagem
acontece de forma individual ao comparar o elemento de rolagem e seus filhos.
Se você rolar um elemento para baixo até 300px
, os filhos dele serão transformados para cima
pelo mesmo valor: 300px
.
No entanto, aplicar um valor de perspectiva ao elemento de rolagem atrapalha
com este processo. ela altera as matrizes que sustentam a transformação de rolagem.
Agora, uma rolagem de 300px só pode mover os filhos em 150px, dependendo do
Os valores perspective
e translateZ
que você escolheu. Se um elemento tiver um
translateZ
com o valor 0, a rolagem será de 1:1 (como costumava ser), mas um filho
empurrado em Z para longe da origem da perspectiva será rolado em um
taxa! Resultado: movimento de paralaxe. E, muito importante, isso é tratado como
parte da máquina de rolagem interna do navegador, o que significa que não há
não é necessário ouvir eventos scroll
nem alterar background-position
.
Uma mosca na pomada: Mobile Safari
Há ressalvas para cada efeito, e uma importante para as transformações é sobre a preservação de efeitos 3D em elementos infantis. Se houver elementos no hierarquia entre o elemento com uma perspectiva e seus filhos de paralaxe, a perspectiva 3D é "achatada", o que significa que o efeito é perdido.
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
No HTML acima, o .parallax-container
é novo e vai funcionar de maneira eficaz.
o valor perspective
se nivela, e o efeito paralaxe é perdido. A solução,
na maioria dos casos é bem simples: você adiciona transform-style: preserve-3d
ao elemento, fazendo com que ele se propague efeitos 3D (como nossa perspectiva
valor) que foram aplicados mais acima na árvore.
.parallax-container {
transform-style: preserve-3d;
}
No caso do Mobile Safari, no entanto, as coisas são um pouco mais complicadas.
A aplicação de overflow-y: scroll
ao elemento de contêiner funciona tecnicamente, mas em
o custo de deslizar rapidamente o elemento de rolagem. A solução é adicionar
-webkit-overflow-scrolling: touch
, mas também nivela o perspective
e não haverá paralaxe.
Do ponto de vista do aprimoramento progressivo, isso provavelmente não é um problema. Se não pudermos paralaxe em todas as situações, nosso aplicativo ainda funcionará, mas seria bom encontrar uma solução.
position: sticky
ao resgate!
Na verdade, há alguma ajuda na forma de position: sticky
, que existe para
permitir que os elementos "fiquem" na parte superior da janela de visualização ou em um elemento pai
durante a rolagem. A especificação, como a maioria deles, é bastante pesada, mas contém um
uma pequena pérola útil em:
Isso pode não parecer muito à primeira vista, mas é um ponto importante Essa frase é sobre como, exatamente, a adesão de um elemento é calculado: "o deslocamento é calculado com referência ao ancestral mais próximo com uma caixa de rolagem". Em outras palavras, a distância para mover o elemento fixo (para que ele apareça anexado a outro elemento ou à janela de visualização) é calculada antes da aplicação de qualquer outra transformação, não depois. Isso significa que que, como o exemplo de rolagem anterior, se o deslocamento fosse calculado em 300 px, há uma nova oportunidade de usar perspectivas (ou qualquer outra transformação) para manipular o valor de deslocamento de 300 px antes de aplicá-lo a qualquer os elementos.
Ao aplicar position: -webkit-sticky
ao elemento de paralaxe, podemos
efetivamente “reverter” o efeito de achatamento de -webkit-overflow-scrolling:
touch
. Isso garante que o elemento de paralaxe faça referência ao objeto
ancestral com uma caixa de rolagem, que nesse caso é .container
. Depois,
Assim como antes, o .parallax-container
aplica um valor perspective
,
que altera o deslocamento de rolagem calculado e cria um efeito paralaxe.
<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);
}
Isso restaura o efeito paralaxe para o Mobile Safari, que é uma excelente notícia. rodada!
Ressalvas de posicionamento fixas
No entanto, há uma diferença aqui: position: sticky
altera o
mecânica de paralaxe. O posicionamento fixo tenta fixar o elemento no
contêiner de rolagem, enquanto uma versão não fixa não. Isso significa que
o efeito paralaxe com fixo acaba sendo o inverso do sem:
- Com
position: sticky
, quanto mais próximo o elemento estiver de z=0, menos ele se move. - Sem
position: sticky
, quanto mais próximo o elemento estiver de z=0, mais eles se movem.
Se tudo isso parece um pouco abstrato, veja esta demonstração de Robert Flack, que demonstra como os elementos se comportam de maneira diferente com e sem elementos fixos de posicionamento. Para ver a diferença, você precisa do Chrome Canary (que é a versão 56) no momento em que este artigo foi escrito) ou Safari.
Uma demonstração de Robert Flack mostrando como
position: sticky
afeta a rolagem de paralaxe.
Vários bugs e soluções alternativas
No entanto, como em qualquer caso, ainda há protuberâncias e protuberâncias que precisam ser suavizado:
- O suporte fixo é inconsistente. O suporte ainda está sendo implementado nos
O Chrome, o Edge não têm suporte total, e o Firefox apresenta bugs de pintura quando o "sticky" é combinado com transformações de perspectiva (link em inglês). Dessa forma,
casos, vale a pena adicionar um pequeno código para adicionar apenas
position: sticky
(os versão com prefixo-webkit-
) quando necessário (para o Mobile Safari) . - O efeito não "simplesmente funciona" no Edge. O Edge tenta processar a rolagem no nível do SO, o que geralmente é bom, mas nesse caso, impede que desde a detecção das mudanças de perspectiva durante a rolagem. Para corrigir isso, você pode adicionar Um elemento de posição fixa, já que parece trocar o Edge para um método de rolagem que não é do SO, e garante que ela considere as mudanças de perspectiva.
- "O conteúdo da página ficou enorme!" Muitos navegadores são responsáveis pela escala
ao decidir o tamanho do conteúdo da página, mas, infelizmente, o Chrome e o Safari
não consideram a perspectiva. Então,
se houver uma escala de 3x aplicada a um elemento, você poderá
ver barras de rolagem e similares, mesmo que o elemento esteja a 1x depois do
perspective
foi aplicado. Para contornar esse problema, dimensionar elementos do canto inferior direito (comtransform-origin: bottom right
), o que funciona porque fará com que elementos muito grandes cresçam na "região negativa" (normalmente no canto superior esquerdo) da área rolável; rolável As regiões nunca permitem que você acesse ou role até o conteúdo da região negativa.
Conclusão
O paralaxe é um efeito divertido quando usado com cuidado. Como você pode ver, é possível para implementá-lo de uma forma que seja eficiente, com acoplamento de rolagem e entre navegadores. Como isso requer um pouco de distorção matemática e uma pequena quantidade para obter o efeito desejado, criamos uma pequena biblioteca auxiliar e um exemplo, que podem ser encontrados no nosso repositório de exemplos de elementos da interface do GitHub (link em inglês).
Curta o jogo e conte para nós como foi o processo.