Conteúdo deste artigo
Se você já brincou bastante com a transição de visualização, deve ter notado que as transições 3D entre duas páginas (ou seja, transições de visualização entre documentos) parecem não funcionar. Isto é, pelo menos não sem que os navegadores nivelem as coisas primeiro.
Os elementos de imagem são o melhor exemplo para demonstrar isso porque, assim como os instantâneos que um navegador tira dos estados antes e depois em uma transição de visualização, as imagens são elementos substituídos então, em teoria, deveríamos ser capazes de usá-los como uma espécie de caso de teste reduzido para animações 3D. Por exemplo, virar uma imagem para revelar outra ao clicar é assim:
É importante observar que, para que a animação funcione corretamente, precisamos definir a propriedade perspective no contêiner pai da imagem (no nosso caso, é o elemento .scene ). Caso contrário, a transformação 3D será meramente plana. É uma espécie de ângulos a aparência do elemento:
Em CSS, o persepective do pai é aplicado a todos os seus filhos , excluindo a si mesmo:
.scene {
perspective: 1200px;
.card { /* gets perspective */ }
}
O que é importante aqui é a estrutura HTML. Especificamente, como o contêiner .scene fica em cima dos elementos filhos .card, dando vida ao efeito 3D para que o flip fique como deveria:
<div class="scene">
<div class="card">
<!-- Card Content Here -->
</div>
</div>
Talvez nossa animação de quadro-chave para inverter o .cards seja algo assim:
@keyframes flipOut {
from {
transform: rotateY(0deg);
}
to {
transform: rotateY(-90deg);
}
}
Que aplicamos ao .cards assim:
.card.flip-out {
animation: flipOut 5.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.card.flip-in {
animation: flipOut 5.2s cubic-bezier(0.4, 0, 0.2, 1) forwards reverse;
}
…onde a animação é executada forwards quando a classe .flip-out é anexada ao .card (cortesia de JavaScript observando um clique) e é executado em reverse quando o .flip-in classe é anexada.
Essa é a configuração de como uma transição de visualização entre documentos também deve funcionar, certo? Se uma imagem suportar uma animação 3D, um instantâneo de transições de visualização deverá fazer o mesmo. Vamos cutucar isso.
Configurando a transição de visualização
Primeiramente, precisamos ativar a visualização de transições em ambas as páginas com a regra ###CODE_a54780d210ec91b26e10dafe9e33a336### definindo o descritor navigation para auto:
@view-transition {
navigation: auto;
}
Se não fizermos mais nada, uma página desaparecerá em outra ao navegar entre as duas. É a mais básica de todas as transições de visualização entre documentos.
Como personalizamos as coisas? Usamos as pseudoclasses ::view-transition-old() e ::view-transition-new() , onde a primeira é o snapshot “antigo” e a última é o “novo”. Assim como os elementos .card que usamos no último exemplo, é onde definimos a animação do quadro-chave:
::view-transition-old(root) {
/* animation goes here */
}
::view-transition-new(root) {
/* animation goes here */
}
O parâmetro root informa à transição de visualização para direcionar a página inteira e todos os elementos criados (e não criados) pelo grupo de instantâneos padrão da transição de visualização.
Aqui está o problema
Digamos que queremos aplicar a mesma inversão 3D a toda a página da web, onde o instantâneo da página “antiga” vira para a página “nova”. Novamente, uma animação 3D nos pede duas coisas:
- A propriedade
perspectiveno elemento pai para que seus filhos obtenham aquele efeito 3D - Uma animação na página para quando a transição de visualização acontece
Mas: Em que exatamente definimos a perspectiva, como em qual é o elemento pai aqui?
Como as transições de visualização tiram instantâneos de toda a página da web , podemos assumir (logicamente) que seria o elemento <html> (ou o :root), certo? Quero dizer, a árvore DOM fica assim quando uma transição de visualização está presente:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ └─ ::view-transition-image-pair(card)
│ │ ├─ ::view-transition-old(card)
│ │ └─ ::view-transition-new(card)
│ └─ ::view-transition-group(name)
│ └─ ::view-transition-image-pair(name)
│ ├─ ::view-transition-old(name)
│ └─ ::view-transition-new(name)
├─ head
└─ body
└─ …
Portanto, todo o snapshot deve estar onde colocamos o perspective. Certo? Acontece que não.
Na verdade, não faz absolutamente nada! Você fica com isso em vez do lindo flip 3D que pudemos usar nos cartões anteriormente:
Aqui está o código com o qual eu estava trabalhando:
/* Cross-document View Transition opt-in */
@view-transition {
navigation: auto;
}
/* 3D flip: Old page flips away, new page flips in */
@keyframes flip-out {
0% {
transform: rotateY(0deg);
opacity: 1;
}
100% {
transform: rotateY(-90deg);
opacity: 0;
}
}
@keyframes flip-in {
0% {
transform: rotateY(90deg);
opacity: 0;
}
100% {
transform: rotateY(0deg);
opacity: 1;
}
}
::view-transition-old(root) {
animation: flip-out 0.3s cubic-bezier(0.4, 0, 1, 1) forwards;
transform-origin: center center;
}
::view-transition-new(root) {
animation: flip-in 0.3s cubic-bezier(0, 0, 0.6, 1) 0.3s backwards;
transform-origin: center center;
}
Observação: Não inverti a animação aqui, já que mudamos para -90deg e então de 90deg. Não é exatamente o mesmo!
E não funciona, não importa se perspective está ligado html ou :root:
/* 👎 */
html {
perspective: 1100px;
}
/* 👎 */
:root {
perspective: 1100px;
}
Fiz algumas pesquisas e descobri que perspective (e transformações 3D em geral) é uma das várias propriedades CSS que produziriam um efeito incomum . (Deixe que Bramus tenha a resposta!)
Então… O que fazemos? Algumas ideias vieram à mente, mas infelizmente falharam:
- Tentei definir a propriedade
perspectivenobody. - Tentei configurar
perspectivedentro de::view-transition-group(root). - Tentei configurar
perspectivedentro de::view-transitionpseudo.
Na verdade, existe uma solução super simples para isso, e não acredito que demorei tanto para descobrir. Não use perspective!
A solução
Breve história: temos que usar a função ###CODE_e3774d118f7d0ccdd67800261ec41a38### em vez da propriedade perspective. E não dentro de nenhum dos pseudo-pseudos ::view-transition-* como você poderia esperar, mas dentro da animação @keyframes :
@keyframes flip-out {
0% {
transform: perspective(1100px) rotateY(0deg);
opacity: 1;
}
100% {
transform: perspective(1100px) rotateY(-90deg);
opacity: 0;
}
}
@keyframes flip-in {
0% {
transform: perspective(1100px) rotateY(90deg);
opacity: 0;
}
100% {
transform: perspective(1100px) rotateY(0deg);
opacity: 1;
}
}
Esta mudança simples, mas grande, move a cena de um plano meh para um lindo ah sim :
Aqui está o porquê, aparentemente. A árvore de pseudoelementos de transição de visualização é renderizada fora do fluxo HTML normal. Mais especificamente, toda a árvore de transição de visualização é renderizada acima do DOM em sua própria camada . No entanto, especialmente para ::view-transition, não sei muito bem por que isso acontece, mas meu melhor palpite seria que cada grupo de transição de visualização tem automaticamente sua posição e valores de transformação substituídos pelo navegador ; portanto, interferindo com o perspective.
A diferença entre perspective e perspective()? A propriedade perspective é aplicada ao elemento pai, enquanto perspective() é um transform função de propriedade aplicada diretamente ao próprio elemento. E como a pseudoárvore de transição de visualização não tem um pai verdadeiro, precisamos usar perspective() já que não requer um pai. Ufa.
Para recapitular…
Configurando perspective ativado o html, :root ou qualquer pseudoclasse de transição de visualização não funcionará. E se você está lutando para encontrar a solução, como eu, acho que essa pequena, mas grande mudança perspective() resolverá esse problema se você se deparar com ela. Acredite em mim, lutei contra isso por semanas até voltar hoje para reclamar sobre isso e descobrir uma solução para isso. Uma vantagem de escrever!
Por que minha transição de visualização 3D não está funcionando? originalmente escrito à mão e publicado com amor em CSS-Tricks . Você realmente deveria receber o boletim informativo também.
