Mejorar el flujo de usuarios a través de las transiciones de página
Las transiciones entre páginas pueden mejorar la experiencia al retener el contexto del usuario, mantener su atención y brindar continuidad visual y retroalimentación positiva, al mismo tiempo que son estéticamente agradables y divertidas y pueden reforzar la marca cuando se hacen bien. En este artículo, Luigi De Rosa creará, paso a paso, una transición entre páginas. También hablará de los pros y los contras de esta técnica y de cómo llevarla al límite.
Cada vez que se interrumpe la experiencia de un usuario, aumentan las posibilidades de que se vaya. Cambiar de una página a otra a menudo provocará esta interrupción al mostrar un destello blanco sin contenido, tardar demasiado en cargar o sacar al usuario del contexto en el que se encontraba antes de que se abriera la nueva página.
Las transiciones entre páginas pueden mejorar la experiencia al retener (o incluso mejorar) el contexto del usuario, mantener su atención y brindar continuidad visual y retroalimentación positiva. Al mismo tiempo, las transiciones de página también pueden ser estéticamente agradables y divertidas y pueden reforzar la marca cuando se hacen bien.
En este artículo, crearemos, paso a paso, una transición entre páginas. También hablaremos de los pros y los contras de esta técnica y de cómo llevarla al límite.
Ejemplos
Muchas aplicaciones móviles hacen buen uso de las transiciones entre vistas. En el siguiente ejemplo, que sigue las pautas de diseño de materiales de Google , vemos cómo la animación transmite relaciones jerárquicas y espaciales entre páginas.
y es posible debido a la naturaleza de propagación de eventos de la API HTML DOM.
Obtener la página
Ahora que hemos interrumpido el navegador cuando intenta cambiar la página, podemos recuperar esa página manualmente usando Fetch API . Veamos la siguiente función, que recupera el contenido HTML de una página cuando se le proporciona su URL.
function loadPage(url) { return fetch(url, { method: 'GET' }).then(function(response) { return response.text(); });}
Para los navegadores que no admiten la API Fetch, considere agregar el polyfill o usar el antiguo XMLHttpRequest
.
Cambiar la URL actual
HTML5 tiene una API fantástica llamada pushState
, que permite a los sitios web acceder y modificar el historial del navegador sin cargar ninguna página. A continuación, lo usamos para modificar la URL actual para que sea la URL de la página siguiente. Tenga en cuenta que esta es una modificación de nuestro controlador de eventos de clic de anclaje previamente declarado.
if (el) { e.preventDefault(); history.pushState(null, null, el.href); changePage(); return;}
Como habrás notado, también hemos agregado una llamada a una función denominada changePage
, que veremos en detalle en breve. También se llamará a la misma función en el popstate
evento, que se activa cuando cambia la entrada del historial activo del navegador (como cuando un usuario hace clic en el botón Atrás de su navegador):
window.addEventListener('popstate', changePage);
Con todo esto, básicamente estamos construyendo un sistema de enrutamiento muy primitivo, en el que tenemos modos activo y pasivo.
Nuestro modo activo se usa cuando un usuario hace clic en un enlace y cambiamos la URL usando , mientras que el modo pasivo se usa cuando la URL cambia y el evento pushState
nos notifica . popstate
En cualquier caso, llamaremos a changePage
, que se encarga de leer la nueva URL y cargar la página correspondiente.
Analizar y agregar el nuevo contenido
Normalmente, las páginas por las que se navega tendrán elementos comunes, como header
y footer
. Supongamos que usamos la siguiente estructura DOM en todas nuestras páginas (que en realidad es la estructura de Smashing Magazine):
var main = document.querySelector('main');function changePage() { // Note, the URL has already been changed var url = window.location.href; loadPage(url).then(function(responseText) { var wrapper = document.createElement('div'); wrapper.innerHTML = responseText; var oldContent = document.querySelector('.cc'); var newContent = wrapper.querySelector('.cc'); main.appendChild(newContent); animate(oldContent, newContent); });}
¡Animar!
Cuando el usuario hace clic en un enlace, la changePage
función busca el HTML de esa página, luego extrae el cc
contenedor y lo agrega al main
elemento. En este punto, tenemos dos cc
contenedores en nuestra página, el primero perteneciente a la página anterior y el segundo de la página siguiente.
La siguiente función, animate
, se encarga de fundir los dos contenedores superponiéndolos, desvaneciendo el antiguo, desvaneciendo el nuevo y eliminando el contenedor antiguo. En este ejemplo, estoy usando la API de animaciones web para crear la animación de desvanecimiento, pero, por supuesto, puedes usar cualquier técnica o biblioteca que desees.
function animate(oldContent, newContent) { oldContent.style.position = 'absolute'; var fadeOut = oldContent.animate({ opacity: [1, 0] }, 1000); var fadeIn = newContent.animate({ opacity: [0, 1] }, 1000); fadeIn.onfinish = function() { oldContent.parentNode.removeChild(oldContent); };}
El código final está disponible en GitHub .
. Esta técnica funciona plenamente como una mejora progresiva . Las páginas aún se muestran y son accesibles de la forma habitual, y el sitio web seguirá funcionando normalmente cuando JavaScript esté deshabilitado.
Si está utilizando un marco SPA, considere usar la navegación PJAX en su lugar, solo para mantener la navegación rápida. Al hacerlo, obtiene soporte heredado y crea un sitio web más compatible con SEO.
Yendo aún más lejos
Podemos seguir superando el límite de esta técnica optimizando ciertos aspectos de la misma. Los siguientes trucos acelerarán la navegación, mejorando significativamente la experiencia del usuario.
Usando un caché
Al cambiar ligeramente nuestra loadPage
función, podemos agregar un caché simple, que garantiza que las páginas que ya han sido visitadas no se vuelvan a cargar.
var cache = {};function loadPage(url) { if (cache[url]) { return new Promise(function(resolve) { resolve(cache[url]); }); } return fetch(url, { method: 'GET' }).then(function(response) { cache[url] = response.text(); return cache[url]; });}
Como habrás adivinado, podemos usar un caché más permanente con la API de caché u otro caché de almacenamiento persistente del lado del cliente (como IndexedDB).
Animar la página actual
Nuestro efecto de fundido cruzado requiere que la siguiente página esté cargada y lista antes de que se complete la transición. Con otro efecto, es posible que deseemos comenzar a animar la página anterior tan pronto como el usuario haga clic en el enlace, lo que le brindaría retroalimentación inmediata, una gran ayuda para el rendimiento percibido.
Al utilizar promesas , manejar este tipo de situaciones resulta muy fácil. El .all
método crea una nueva promesa que se resuelve tan pronto como se resuelven todas las promesas incluidas como argumentos.
// As soon as animateOut() and loadPage() are resolved…Promise.all[animateOut(), loadPage(url)] .then(function(values) { …
Precargando la página siguiente
Usando solo la navegación PJAX, los cambios de página suelen ser casi dos veces más rápidos que la navegación predeterminada, porque el navegador no tiene que analizar ni evaluar ningún script o estilo en la nueva página.
Sin embargo, podemos ir aún más lejos al comenzar a precargar la página siguiente cuando el usuario pasa el cursor sobre el enlace o comienza a tocarlo.
.)
Partial Output
In our loadPage
function, we are fetching the entire HTML document, but we actually just need the cc
container. If we are using a server-side language, we can detect whether the request is coming from a particular custom AJAX call and, if so, output just the container it needs. By using the Headers API, we can send a custom HTTP header in our fetch request.
function loadPage(url) { var myHeaders = new Headers(); myHeaders.append('x-pjax', 'yes'); return fetch(url, { method: 'GET', headers: myHeaders, }).then(function(response) { return response.text(); });}
Then, on the server side (using PHP in this case), we can detect whether our custom header exists before outputting only the required container:
if (isset($_SERVER['HTTP_X_PJAX'])) { // Output just the container}
This will reduce the size of the HTTP message and also reduce the server-side load.
Wrapping Up
After implementing this technique in a couple of projects, I realized that a reusable library would be immensely helpful. It would save me time in implementing it on each occasion, freeing me to focus on the transition effects themselves.
Thus was born Barba.js, a tiny library (4 KB minified and gZip’d) that abstracts away all of this complexity and provides a nice, clean and simple API for developers to use. It also accounts for views and comes with reusable transitions, caching, prefetching and events. It is open source and available on GitHub.
(rb, ml, al, il, mrn)Explore more on
- Coding
- UX
- Mobile
- JavaScript
Deja un comentario