Buy Access to Course
15.

Ver Transiciones

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Día 15 Ya hemos llegado a la mitad de nuestra aventura. Y a partir de ahora la cosa no hará más que enfriarse.

Para celebrarlo, hoy trabajaremos en algo nuevo y brillante: la API de Transiciones de Vista. Esta ingeniosa novedad es compatible con la mayoría de los navegadores y nos permite realizar transiciones suaves cada vez que cambia el HTML de nuestra página.

Tip

En realidad, a fecha de diciembre de 2023, las transiciones de vista sólo están soportadas en Chrome, aunque se dice que está prevista su compatibilidad con Firefox y Safari.

Y si tu usuario tiene un navegador que no lo soporta, ¡no pasa nada! La transición simplemente se salta, pero todo sigue funcionando. No pasa nada.

Ah, y las Transiciones de Vista vendrán de serie en Turbo 8 para la navegación por toda la página, aunque las llevaremos un poco más lejos.

Marcianos malvados y demostración de las transiciones de vista

Para utilizar las Transiciones de Vista, no necesitas ninguna biblioteca externa. Pero vamos a utilizar una llamada "turbo view transitions". Esto surgió de una serie de blogs en los que el autor construyó un estupendo proyecto llamado Turbo Music Drive. En dos entradas del blog Evil Martians, hablan de morphing y de hacer locuras con él en Turbo. ¡Incluso crearon una demostración en directo!

En su forma más simple, las transiciones de vista añaden un fundido cruzado mientras navegas. Pero también puedes ser más específico y conectar elementos entre páginas para darles una transición especial. Mira: cuando hago clic en este álbum, se mueve hacia arriba a la izquierda. También hay un bonito fundido cruzado para el resto de la página.

Instalación de turbo-view-transitions

¡Vamos a probarlo! Primer paso: consigue la biblioteca turbo-view-transitions. En tu terminal, ejecuta:

php bin/console importmap:require turbo-view-transitions

¡Encantador! Para activar las transiciones, tenemos que hacer dos cosas. Primero, en base.html.twig, añade una etiqueta meta con name="view-transition":

53 lines | templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
// ... lines 4 - 6
<meta name="view-transition">
// ... lines 8 - 14
</head>
// ... lines 16 - 51
</html>

¡Así es como le dices a tu navegador que las quieres!

En segundo lugar, en Turbo 7, tenemos que activar las transiciones en JavaScript. Dirígete a app.js. Ésta será una rara ocasión en la que escribamos JavaScript que no necesite vivir en un controlador Stimulus. Copia de su ejemplo, pega... y mueve el import a la parte superior:

24 lines | assets/app.js
// ... lines 1 - 4
import { shouldPerformTransition, performTransition } from 'turbo-view-transitions';
// ... lines 6 - 9
document.addEventListener('turbo:before-render', (event) => {
if (shouldPerformTransition()) {
event.preventDefault();
performTransition(document.body, event.detail.newBody, async () => {
await event.detail.resume();
});
}
});
document.addEventListener('turbo:load', () => {
// View Transitions don't play nicely with Turbo cache
if (shouldPerformTransition()) Turbo.cache.exemptPageFromCache();
});

¡Listo! ¡Eso debería bastar para que las navegaciones de Turbo Drive utilicen transiciones de vista! Esto aprovecha un evento de Turbo llamado turbo:before-render. La funciónshouldPerformTransition() comprueba si el navegador del usuario admite transiciones. Si no lo hace, es un comportamiento normal. Pero si lo hace, entoncesperformTransition() hará la transición entre el cuerpo antiguo y el nuevo. También hay un poco de código aquí abajo para evitar la caché de la página turbo. Creo que es algo que funcionará mejor en Turbo 8 cuando esto sea oficial.

¡Es hora de la gran revelación! Pulsa actualizar y mira. Oooooh. ¡Una suave transición cruzada! Así que no sólo hemos eliminado las recargas completas de página, sino que ahora tenemos una transición entre nuestras páginas. Cuidado Powerpoint, ¡vamos a por ti!

Transición Turbo Frames

¿Pero qué pasa con los marcos Turbo? Cuando hacemos clic, sigue ocurriendo instantáneamente. Activamos las transiciones para las navegaciones de página completa, pero no para los marcos. ¿Podemos? ¡Claro!

Copia esta escucha, y pégala debajo. Esta vez, escuchaturbo:before-frame-render... y ajusta esta parte. En lugar de document.body, utiliza event.target. Ese será el <turbo-frame>. Y entonces el nuevo elemento será event.detail.newFrame:

34 lines | assets/app.js
// ... lines 1 - 24
document.addEventListener('turbo:before-frame-render', (event) => {
if (shouldPerformTransition()) {
event.preventDefault();
performTransition(event.target, event.detail.newFrame, async () => {
await event.detail.resume();
});
}
});

¡Listo! Actualiza y haz clic en .... . Transición, ¡comprobada!

Depuración de transiciones

Y si la transición no es lo suficientemente obvia, puedes abrir las herramientas de tu navegador, hacer clic en el pequeño "...", ir a "más herramientas" y luego a Animaciones. Parece que ya lo tenía abierto. Aquí puedes cambiar la velocidad de tus animaciones.

Ahora... es super obvio. Incluso puedes ver cómo funciona. Si te desplazas durante la transición, puedes ver cómo toma una captura de pantalla del HTML antiguo y lo mezcla con el nuevo. Este efecto raro no suele ser un problema porque ocurre muy rápido, pero es chulo de ver.

Caso extremo: Marcos que avanzan

Para mostrar un problema, vamos a eliminar la transición CSS de la tabla que acabamos de añadir. Así que gira hasta esa plantilla... y quita el class:

139 lines | templates/main/homepage.html.twig
// ... lines 1 - 27
{% block body %}
<div class="flex">
// ... lines 30 - 36
<section class="flex-1 ml-10">
// ... lines 38 - 56
<turbo-frame id="voyage-list" data-turbo-action="advance">
// ... lines 58 - 134
</turbo-frame>
</section>
</div>
{% endblock %}

Actualizar... y pruébalo. No pasa nada. No pasa nada. Es decir, funciona... pero no hay transición de vista. ¿Por qué?

Esto es sutil. La transición se rompe cuando tienes un fotograma que hace avanzar la URL. El problema es que, en esta situación, Turbo llama a turbo:before-frame-render... y también llama a turbo:before-render justo después. Estas dos, más o menos, chocan.

Pero tiene fácil solución. Crea una variable: let skipNextRenderTransition y ajústala a false. A continuación, si shouldPerformTransition() y no skipNextRenderTransition, entonces hazlo:

42 lines | assets/app.js
// ... lines 1 - 9
let skipNextRenderTransition = false;
document.addEventListener('turbo:before-render', (event) => {
if (shouldPerformTransition() && !skipNextRenderTransition) {
// ... lines 13 - 17
}
});
// ... lines 20 - 42

Por último, cuando nuestro fotograma inicie su transición, establece esta variable en true. Incluye también una setTimeout(), ponla de nuevo a false y retrasa esto 100 milisegundos:

42 lines | assets/app.js
// ... lines 1 - 25
document.addEventListener('turbo:before-frame-render', (event) => {
if (shouldPerformTransition()) {
// ... lines 28 - 29
// workaround for data-turbo-action="advance", which triggers
// turbo:before-render (and we want THAT to not try to transition)
skipNextRenderTransition = true;
setTimeout(() => {
skipNextRenderTransition = false;
}, 100);
// ... lines 36 - 39
}
});

Es un poco raro. Pero, ¡eso es programar! Establecemos esto en true, Turbo activa el otro oyente casi inmediatamente... y luego a los 100 milisegundos lo reiniciamos. Probablemente también podríamos sustituir el setTimeout() configurando skipNextRenderTransition = falseen el oyente turbo:before-render.

Lo más importante es que... ¡ahora tenemos una transición! Volvamos a ponerlo a toda velocidad. ¡Me gusta!

Desactivar transiciones

Prueba con el marco emergente del planeta. ¡Vaya! Eso ha sido raro. Para ser totalmente sincero, no sé qué está pasando aquí. Por alguna razón, la transición de la vista hace que desaparezca el popover... lo cual no es... digamos... lo ideal. Probablemente haya una forma de arreglarlo, pero no he podido descifrarla.

No pasa nada... y creo que esta rareza está aislada del comportamiento del popover. En su lugar, añadiremos una forma de desactivar las transiciones en un marco.

En la página de inicio, busca turbo-frame. Aquí está. Añade un nuevo atributo llamadodata-skip-transition:

139 lines | templates/main/homepage.html.twig
// ... lines 1 - 27
{% block body %}
<div class="flex">
// ... lines 30 - 36
<section class="flex-1 ml-10">
// ... lines 38 - 56
<turbo-frame id="voyage-list" data-turbo-action="advance">
<div class="bg-gray-800 p-4 rounded">
<table class="w-full text-white">
// ... lines 60 - 82
<tbody>
{% for voyage in voyages %}
<tr class="border-b border-gray-700 {% if loop.index is odd %} bg-gray-800 {% else %} bg-gray-700 {% endif %}">
// ... line 86
<td class="px-2 whitespace-nowrap">
<div
data-controller="popover"
data-action="mouseenter->popover#show mouseleave->popover#hide"
class="relative"
>
// ... lines 93 - 98
<template data-popover-target="content">
<div
data-popover-target="card"
class="max-w-sm rounded shadow-lg bg-gray-900 absolute left-0 bottom-10"
>
<turbo-frame data-skip-transition id="planet-card-{{ voyage.planet.id }}" target="_top" src="{{ path('app_planet_show_card', {
'id': voyage.planet.id,
}) }}">
// ... lines 107 - 112
</turbo-frame>
</div>
</template>
</div>
</td>
// ... line 118
</tr>
{% endfor %}
</tbody>
</table>
</div>
// ... lines 124 - 134
</turbo-frame>
</section>
</div>
{% endblock %}

Me lo he inventado. Sobre un app.js, podemos buscarlo. Si esshouldPerformTransition() y no event.target.hasAttribute('data-skip-transition'), entonces haz la transición:

42 lines | assets/app.js
// ... lines 1 - 25
document.addEventListener('turbo:before-frame-render', (event) => {
if (shouldPerformTransition() && !event.target.hasAttribute('data-skip-transition')) {
// ... lines 28 - 39
}
});

Ahora... ¡arreglado! Y tenemos transiciones en... prácticamente todos los tipos de navegación de nuestro sitio. ¡Y en sólo unos 10 minutos! ¡Es una locura!

Ahora pasamos a la misión de mañana: crear un deslumbrante sistema de notificaciones tostadas que sea fácil de activar, independientemente de dónde y cómo tengamos que añadirlas. ¡Hasta entonces!