¿Por qué no funciona mi transformación de vista 3D?
Si trabaja con transiciones de vista con frecuencia, es posible que haya notado que las transiciones 3D entre dos páginas (es decir, transiciones de vista entre archivos) no parecen funcionar. Dicho esto, al menos los navegadores no aplanan las cosas primero.
El elemento de imagen es el mejor ejemplo para demostrar esto, porque así como el navegador toma una instantánea del estado antes y después de una transición de vista, la imagen es el elemento que se reemplaza Entonces, en teoría, deberíamos poder usarlos como un caso de prueba simplificado para la animación 3D. Por ejemplo, voltea una imagen para revelar otra imagen al hacer clic, así:
Es importante tener en cuenta que para que la animación funcione correctamente debemos configurar perspective Propiedad en el contenedor principal de la imagen (en nuestro caso es .scene elemento). De lo contrario, la transformación 3D es sólo plana. un poco como ángulo Aspecto del elemento:
En CSS, los padres persepective Sí se aplica a todos sus hijosexcluyéndose a sí mismo:
.scene {
perspective: 1200px;
.card { /* gets perspective */ }
}
Lo que importa aquí es la estructura HTML. ¿Cómo exactamente? .scene El contenedor se coloca encima del niño. .card elementos para darle vida al efecto 3D, por lo que el flip debería verse así:
<div class="scene">
<div class="card">
<!-- Card Content Here -->
</div>
</div>
Quizás sea necesario invertir nuestra animación de fotogramas clave. .cards Dice así:
@keyframes flipOut {
from {
transform: rotateY(0deg);
}
to {
transform: rotateY(-90deg);
}
}
Lo aplicamos a .cards Como esto:
.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;
}
…donde funciona la animación forwards cuando .flip-out Las categorías se adjuntan a .card (proporcionado por clics de monitoreo de JavaScript) y se ejecuta en reverse cuando .flip-in Se adjuntan categorías.
Esta es también la configuración de cómo deberían funcionar las transiciones de vista entre archivos, ¿verdad? Si la imagen admite animación 3D, la instantánea de transición de vista debería hacer lo mismo. Exploremoslo.
Establecer transición de vista
Primero tenemos que seleccionar ver transiciones en ambas páginas. @view-transition reglas establecidas por navigation descriptor de auto:
@view-transition {
navigation: auto;
}
Si no hacemos nada más, una página se desvanecerá en la otra al navegar entre las dos páginas. es lo mas basico Todas las conversiones de vistas entre documentos.
¿Cómo ordenamos las cosas? usamos ::view-transition-old() y ::view-transition-new() Pseudoclases, donde la primera es la instantánea “antigua” y la segunda es la instantánea “nueva”. como .card El elemento que usamos en el ejemplo anterior, aquí es donde configuramos la animación de fotogramas clave:
::view-transition-old(root) {
/* animation goes here */
}
::view-transition-new(root) {
/* animation goes here */
}
este root El argumento le indica a la transición de vista que apunte a toda la página y a todos los elementos creados (y no creados) por el grupo de instantáneas preestablecido de la transición de vista.
El problema es este
Supongamos que queremos aplicar el mismo giro 3D a toda la página web, donde una instantánea de la página “antigua” pasa a la página “nueva”. Asimismo, la animación 3D requiere que hagamos dos cosas:
- este
perspectiveAtributo en el elemento padre para que sus elementos hijos obtengan un efecto 3D - Animaciones en la página cuando ocurren transiciones de vista.
Pero: ¿qué perspectiva queremos establecer, por ejemplo, cuál es el elemento padre aquí?
desde Ver transición toma una instantánea de toda la página webpodemos suponer (lógicamente) que será <html> elemento (o :root), ¿correcto? Lo que quiero decir es que cuando hay una transición de vista, el árbol DOM se ve así:
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
└─ …
Entonces, la instantánea completa debería estar donde colocamos perspective. ¿Correcto? Resulta que no.
De hecho, ¡no se hizo nada! En lugar del bonito giro 3D que usamos antes en la tarjeta, te queda esto:
Este es el código que estoy usando:
/* 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;
}
notas: No estoy invirtiendo la animación aquí porque cambiamos a -90deg luego de 90deg. ¡No es exactamente lo mismo!
Y no funciona de todos modos perspective Ya encendido html o :root:
/* 👎 */
html {
perspective: 1100px;
}
/* 👎 */
:root {
perspective: 1100px;
}
Investigué un poco y encontré perspective (y las transformaciones 3D en general) son varias Propiedades CSS que producen efectos inusuales.. (¡Que responda Brahms!)
Entonces… ¿qué hacemos? Se me ocurrieron algunas ideas pero desafortunadamente fallé:
- trato de configurar
perspectivepropiedad enbody. - trato de configurar
perspectiveen::view-transition-group(root). - trato de configurar
perspectiveadentro::view-transitionFalso.
En realidad, hay una solución súper fácil. No puedo creer que me haya llevado tanto tiempo descubrirla. No la uses. perspective ¡fundamental!
solución
Cuento corto: Debemos usar perspective() funcionar en lugar de perspective propiedad. y no dentro de ninguna ::view-transition-* Como era de esperar, pero por dentro. @keyframes Dibujos animados:
@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;
}
}
Este cambio simple pero dramático traslada la escena de un apartamento a Eh a una hermosa Ah si:
Al parecer, esa es la razón. Se representa el árbol de pseudoelementos de transición de vista. externo Flujo HTML normal. Más específicamente, todo el árbol de transformación de vistas es Renderizado en su propia capa encima del DOM. Sin embargo, especialmente para ::view-transitionNo estoy muy seguro de por qué sucede esto, pero mi mejor conjetura es que cada El navegador sobrescribe automáticamente los valores de posición y transformación del grupo de transición de vista.;Por lo tanto, la interferencia perspective.
la diferencia entre perspective y perspective()? este perspective Los atributos se aplican al elemento padre, mientras que perspective() es un transform Las funciones de atributos se aplican directamente al propio elemento. Dado que el pseudoárbol de transformación de vista no tiene un árbol padre real, tenemos que usar perspective() Porque no necesita padres. Uf.
Para revisar…
ambiente perspective En html, :rooto cualquier pseudoclase de conversión de vistas no funcionará. Si eres como yo y has estado luchando por encontrar una solución, creo que es un problema pequeño pero importante. perspective() Si tiene este problema, los cambios lo solucionarán. Créanme, luché con este problema durante semanas, hasta que hoy volví quejándome y encontré una solución. ¡Las ventajas de escribir!