Transizioni Animate in MPA con la View Transitions API
By
•
13 luglio 2025
•
5 minuti di lettura
Navigando su questo sito, potresti notare che le animazioni non si limitano agli elementi di una singola pagina. Ad esempio, quando selezioni un articolo dalla lista , la sua immagine in miniatura si espande fluidamente per diventare l’hero image nella pagina di dettaglio.
Questo avviene nonostante il sito utilizzi un’architettura tradizionale Multi-Page Application (MPA), dove ogni navigazione comporta il caricamento di una nuova pagina.
Queste transizioni, come quella che vedi nell’esempio interattivo di carrello e-commerce qui sotto, sono ottenute tramite Progressive Enhancement utilizzando la View Transitions API. Il concetto di progressive enhancement è fondamentale: la funzionalità viene aggiunta come un livello migliorativo.
Se il browser dell’utente la supporta, l’animazione avviene;
In caso contrario, l’utente visualizza semplicemente una navigazione standard senza alcuna rottura o perdita di performance.
Un ulteriore vantaggio è che l’API rispetta nativamente le preferenze dell’utente. Se nel sistema operativo è attiva l’opzione per la riduzione del movimento (prefers-reduced-motion), le transizioni vengono disabilitate in automatico, garantendo accessibilità senza scrivere una riga di codice in più.
Carrello
Bottiglia di Skooma
25.00
Spada Imperiale
23.00
Martello da Guerra Daedrico
2500.00
Guanti Elfici
45.00
Elmo di Ferro
60.00
Scudo della Guardia di Windhelm
45.00
Armatura Orchesca
400.00
L’interazione Client-Server: MPA vs. SPA
Per apprezzare l’importanza di questa API, è utile ripercorrere l’evoluzione delle architetture web.
Multi-Page Application (MPA)
Agli albori del web, il modello MPA era l’unico esistente. I browser erano client relativamente semplici, con motori JavaScript poco potenti. Il loro compito principale era renderizzare HTML e CSS inviati dal server.
Di conseguenza, l’intero stato dell’applicazione (chi sei, cosa hai nel carrello, etc.) doveva risiedere e essere gestito quasi esclusivamente sul server. Ad ogni interazione, il browser richiedeva una pagina completamente nuova.
sequenceDiagram
participant Browser as Utente
participant Server as Server
Note over Browser: Utente clicca su un link o invia un form
Browser->>Server: Richiesta HTTP per una nuova pagina (es. GET /prodotti)
activate Server
Note right of Server: Il server recupera lo stato<br/>(es. sessione utente, dati dal DB)<br/>e genera una pagina HTML completa.
Server-->>Browser: Risposta HTTP 200 OK (invia intero file HTML)
deactivate Server
Note over Browser: Il contesto della pagina precedente<br/>viene completamente distrutto.<br/>Il browser renderizza la nuova pagina da zero.
Single-Page Application (SPA)
Con l’evoluzione dei browser e la crescente potenza di JavaScript, è emerso il modello SPA. In questa architettura, una porzione significativa dello stato applicativo viene delegata al client. La pagina HTML viene caricata una sola volta e le interazioni successive aggiornano dinamicamente la vista tramite JavaScript, creando un’esperienza utente fluida e reattiva.
Un’architettura SPA, dove la pagina di base è persistente, permette di manipolare liberamente le risorse allocate dal browser per quel tab. Animare un elemento da uno stato A a uno stato B è quindi un’operazione nativa e relativamente semplice.
sequenceDiagram
participant App JS as JavaScript App
participant Client as Browser
participant Server
%% --- Fase 1: Caricamento Iniziale ---
Note over Client: Utente visita il sito per la prima volta
Client->>Server: GET / (Richiesta HTML Shell)
activate Server
Server-->>Client: HTML Shell (struttura base)
deactivate Server
Client->>Server: GET /app.js (Richiesta Bundle JS)
activate Server
Server-->>Client: Bundle JavaScript completo
deactivate Server
activate App JS
Note over App JS, Client: Boot (hijack routing)
Note over App JS, Client: Render vista iniziale
%% --- Fase 2: Navigazione Interna ---
Note over App JS, Client: Utente clicca un link interno
Note over App JS: Azione intercettata
App JS->>Server: Chiamata API (GET /api/prodotti)
activate Server
Note over Server: Server recupera solo i dati necessari<br/>e risponde con JSON.
Server-->>App JS: Dati in formato JSON
deactivate Server
Note over App JS: Usa il JSON per aggiornare<br/>dinamicamente il DOM della pagina.
Note over App JS, Client: Nessun ricaricamento completo
deactivate App JS
La Sfida delle Transizioni in una MPA
In una MPA, ogni volta che si naviga verso una nuova pagina, il contesto della pagina precedente viene completamente distrutto. Tutte le variabili, gli elementi del DOM e gli stati in memoria vengono eliminati per far posto al nuovo ambiente.
Questo rende impossibile usare JavaScript per animare un elemento tra le due pagine.
Anche se meccanismi come localStorage, sessionStorage e i cookie permettono di mantenere dati tra le navigazioni, non risolvono il problema dell’animazione. Essi persistono dati, non elementi del DOM durante il brevissimo istante della transizione tra il rendering di una pagina e l’altra. Questo è un limite intrinseco del modello di navigazione dei browser, una sandbox le cui regole non possiamo aggirare.
Con poche righe di CSS, è possibile istruire il browser a gestire la transizione di elementi specifici.
1. Attivare le transizioni tra pagine
Il primo passo è abilitare la funzionalità per le navigazioni cross-documento. Questo si fa aggiungendo una semplice regola nel CSS di entrambe le pagine (quella di partenza e quella di arrivo).
Questa regola indica al browser di intercettare le navigazioni tra pagine della stessa origine e di applicare una transizione di default (una dissolvenza incrociata, un fade-in/fade-out).
2. Collegare gli Elementi
Il passaggio chiave è dire al browser quale elemento nella pagina A corrisponde a quale elemento nella pagina B. Questo si ottiene assegnando lo stesso, unico valore alla proprietà CSS view-transition-name a entrambi gli elementi.
Assegnando lo stesso view-transition-name (hero-image-post-123), il browser capisce che questi due elementi sono concettualmente lo stesso e animerà la transizione tra le loro diverse dimensioni e posizioni, creando un effetto fluido e professionale che prima era un’esclusiva delle SPA.
Nota come il nome hero-image-post-123 sia specifico. In un’applicazione reale, questo valore non sarebbe statico ma generato dinamicamente, ad esempio utilizzando l’ID univoco del prodotto o dell’articolo (es. hero-image-post-${post.id}). Questo assicura che ogni elemento della lista punti correttamente e senza ambiguità alla sua controparte nella pagina di destinazione.
Attenzione: La proprietà view-transition-name deve essere unica nel DOM in un dato momento. Se due elementi visibili hanno lo stesso nome, il browser non saprà come gestire la transizione e l’animazione non avverrà.
La generazione dinamica basata su ID risolve proprio questo problema. Puoi infatti creare il valore di view-transition-name in modo dinamico:
Di default, il browser applica una dissolvenza incrociata (cross-fade). Tuttavia, hai il pieno controllo sull’animazione tramite CSS. Utilizzando degli speciali pseudo-elementi, puoi definire animazioni complesse e su misura.
Ad esempio, per modificare l’animazione di default della pagina e farla scorrere, potresti usare:
::view-transition-old(root) { animation: slide-from-right 0.5s ease-out;}::view-transition-new(root) { animation: slide-to-left 0.5s ease-in;}/* E puoi animare in modo specifico l'elemento condiviso! */::view-transition-new(hero-image-post-123) { /* L'animazione di transform (scala, posizione) è gestita dal browser. Qui puoi aggiungere altro, come una transizione sul `border-radius`. */ transition: border-radius 0.5s;}
Questo apre infinite possibilità creative che vanno ben oltre l’effetto standard.