Evita problemi di ritaglio (e altro ancora) nelle transizioni di visualizzazione utilizzando gruppi di transizione di visualizzazione nidificati

Data di pubblicazione: 22 settembre 2025

Quando avvii una transizione di visualizzazione, il browser acquisisce automaticamente snapshot prima e dopo degli elementi taggati con un view-transition-name. Questi snapshot vengono visualizzati in un albero di pseudo-elementi. Per impostazione predefinita, l'albero generato è "piatto". Ciò significa che la gerarchia originale nel DOM viene persa e tutti i gruppi di transizione di visualizzazione acquisiti sono fratelli sotto un unico pseudo-elemento ::view-transition.

Questo approccio ad albero piatto è sufficiente per molti casi d'uso, ma non per alcuni casi d'uso di stile. Di seguito sono riportati alcuni esempi di effetti che possono avere un effetto visivo inaspettato in una struttura ad albero piatta:

  • Ritaglio (overflow, clip-path, border-radius): il ritaglio influisce sui figli dell'elemento, il che significa che gli elementi secondari del gruppo di transizione della visualizzazione non possono ritagliarsi a vicenda.
  • opacity, mask-image e filter: analogamente, questi effetti sono progettati per funzionare su un'immagine rasterizzata completamente di un albero, interessando i figli anziché ogni elemento singolarmente.
  • Trasformazioni 3D (transform-style, transform, perspective): per visualizzare l'intera gamma di animazioni di trasformazione 3D, è necessario mantenere una certa gerarchia.

L'esempio seguente mostra un pseudo-albero piatto, con elementi ritagliati da un elemento principale nell'albero DOM. Questi elementi perdono il ritaglio durante la transizione della visualizzazione, con conseguente effetto visivo interrotto.

Registrazione di un effetto di ritaglio interrotto durante l'esecuzione di una transizione di visualizzazione. Il testo dovrebbe essere troncato dalla finestra di dialogo, ma non lo è. Il tempo di animazione viene rallentato per esagerare l'effetto.

I gruppi di transizioni di visualizzazione nidificati sono un'estensione delle transizioni di visualizzazione che ti consente di nidificare gli pseudo-elementi ::view-transition-group l'uno nell'altro. Quando i gruppi di transizione della visualizzazione sono nidificati, è possibile ripristinare effetti come il ritaglio durante la transizione.

Browser Support

  • Chrome: 140.
  • Edge: not supported.
  • Firefox: not supported.
  • Safari: not supported.

Da una pseudo-struttura ad albero piatta a una pseudo-struttura ad albero nidificata

Nella seguente demo puoi fare clic sull'avatar di una persona per visualizzare ulteriori informazioni. Le animazioni vengono gestite da una transizione di visualizzazione nello stesso documento che trasforma il pulsante su cui è stato fatto clic nella finestra di dialogo, sposta l'avatar e il nome sullo schermo e fa scorrere i paragrafi della finestra di dialogo verso l'alto o verso il basso.

Demo live

Registrazione della demo

Registrazione demo (rallentata)

Se osservi attentamente la demo, noterai che c'è un problema con la transizione: anche se i paragrafi con la descrizione sono elementi secondari dell'elemento <dialog> nel DOM, il testo non viene ritagliato dalla casella <dialog> durante la transizione:

<dialog id="info_bramus" closedby="any">
  <h2><img alt="…" class="avatar" height="96" width="96" src="avatar_bramus.jpg"> <span>Bramus</span></h2>
  <p>Bramus is …</p>
  <p>…</p>
</dialog>

L'applicazione di overflow: clip su <dialog> non ha alcun effetto.

Il problema è il modo in cui le transizioni di visualizzazione creano e visualizzano il loro pseudo albero:

  • Nell'albero pseudo, per impostazione predefinita, tutti gli snapshot sono fratelli tra loro.
  • Lo pseudo-albero viene visualizzato in uno pseudo-elemento ::view-transition che viene visualizzato sopra l'intero documento.

Per questa demo in particolare, l'albero DOM ha il seguente aspetto:

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)
  │  ├─ ::view-transition-group(avatar)
  │  │  └─ ::view-transition-image-pair(avatar)
  │  │     ├─ ::view-transition-old(avatar)
  │  │     └─ ::view-transition-new(avatar)
  │  ├─ ::view-transition-group(paragraph1.text)
  │  │  └─ ::view-transition-image-pair(paragraph1.text)
  │  │     └─ ::view-transition-new(paragraph1.text)
  │  └─ ::view-transition-group(paragraph2.text)
  │     └─ ::view-transition-image-pair(paragraph2.text)
  │        └─ ::view-transition-new(paragraph2.text)
  ├─ head
  └─ body
        └─ …

Poiché gli pseudo-elementi ::view-transition-group(.text) sono fratelli successivi dello pseudo-elemento ::view-transition-group(card), vengono visualizzati sopra la scheda.

Per avere ::view-transition-group(card) clip ::view-transition-group(.text), gli pseudonimi ::view-transition-group(.text) devono essere elementi secondari di ::view-transition-group(card). A questo scopo, utilizza view-transition-group, che ti consente di assegnare un "gruppo principale" a uno pseudo-elemento ::view-transition-group() generato.

Per modificare il gruppo principale, hai due opzioni:

  • Imposta view-transition-group su contain nell'elemento principale in modo che contenga tutti gli elementi secondari con un view-transition-name.
  • Per tutti i publisher secondari, imposta view-transition-group su view-transition-name del publisher principale. Puoi anche utilizzare nearest per scegliere come target il gruppo antenato più vicino.

Quindi, per questa demo, per utilizzare i gruppi di transizione di visualizzazione nidificati, il codice diventa:

button.clicked,
dialog {
  view-transition-group: contain;
}

Oppure

button.clicked,
dialog *,
  view-transition-group: nearest;
}

Con questo codice, gli pseudo-elementi ::view-transition-group(.text) vengono ora nidificati all'interno dello pseudo-elemento ::view-transition-group(card). Questa operazione viene eseguita in uno pseudo ::view-transition-group-children(…) aggiuntivo, che mantiene uniti tutti gli pseudo nidificati:

html
  ├─ ::view-transition
  │  ├─ ::view-transition-group(card)
  │  │  ├─ ::view-transition-image-pair(card)
  │  │  │  ├─ ::view-transition-old(card)
  │  │  │  └─ ::view-transition-new(card)
  │  │  └─::view-transition-group-children(card)
  │  │    ├─ ::view-transition-group(paragraph1.text)
  │  │    │  └─ ::view-transition-image-pair(paragraph1.text)
  │  │    │     └─ ::view-transition-new(paragraph1.text)
  │  │    └─ ::view-transition-group(paragraph2.text)
  │  │       └─ ::view-transition-image-pair(paragraph2.text)
  │  │          └─ ::view-transition-new(paragraph2.text)
  │  ├─ ::view-transition-group(name)
  │  │  └─ ::view-transition-image-pair(name)
  │  │     ├─ ::view-transition-old(name)
  │  │     └─ ::view-transition-new(name)
  │  └─ ::view-transition-group(avatar)
  │     └─ ::view-transition-image-pair(avatar)
  │        ├─ ::view-transition-old(avatar)
  │        └─ ::view-transition-new(avatar)
  ├─ head
  └─ body
        └─ …

Infine, per fare in modo che ::view-transition-group(card) pseudo ritagli i paragrafi, applica overflow: clip a ::view-transition-group-children(card) pseudo:

::view-transition-group-children(card) {
  overflow: clip;
}

Il risultato è il seguente:

Demo live

Registrazione della demo

Registrazione demo (rallentata)

Lo pseudo ::view-transition-group-children è presente solo quando vengono utilizzati gruppi nidificati. Ha le dimensioni della border-box dell'elemento originale e ha un bordo trasparente con la stessa forma e lo stesso spessore del bordo dell'elemento che ha generato lo pseudo elemento, ovvero card nell'esempio precedente.

Ritagli e altro

I gruppi di transizioni di visualizzazione nidificati vengono utilizzati in posizioni diverse dagli effetti di ritaglio. Un altro esempio sono gli effetti 3D. Nella seguente demo è presente un'opzione per ruotare la scheda in 3D durante la transizione.

html:active-view-transition-type(open) {
    &::view-transition-old(card) {
        animation-name: rotate-out;
    }
    &::view-transition-new(card) {
        animation-name: rotate-in;
    }
}
html:active-view-transition-type(close) {
    &::view-transition-old(card) {
        animation-name: rotate-in;
    }
    &::view-transition-new(card) {
        animation-name: rotate-out;
    }
}

Senza gruppi di transizione di visualizzazione nidificati, l'avatar e il nome non ruotano insieme alla scheda.

Demo live

Registrazione della demo

Registrazione demo (rallentata)

Se nidifichi gli pseudoelementi dell'avatar e del nome all'interno della scheda, l'effetto 3D può essere ripristinato. Ma non è l'unica cosa che devi fare. Oltre a ruotare gli pseudonimi ::view-transition-old(card) e ::view-transition-new(card), devi ruotare anche quello ::view-transition-group-children(card).

html:active-view-transition-type(open) {
    &::view-transition-group-children(card) {
        animation: rotate-in var(--duration) ease;
        backface-visibility: hidden;
    }
}
html:active-view-transition-type(close) {
    &::view-transition-group-children(card) {
        animation: rotate-out var(--duration) ease;
        backface-visibility: hidden;
    }
}

Demo live

Registrazione della demo

Registrazione demo (rallentata)

Altre demo

Nel seguente esempio, i gruppi di transizione delle visualizzazioni nidificate vengono utilizzati per assicurarsi che le schede vengano ritagliate dallo scroller principale. Puoi attivare o disattivare l'utilizzo di gruppi di transizione di visualizzazione nidificati utilizzando i controlli inclusi.

Demo live

Registrazione della demo

L'aspetto interessante di questa demo è che tutti gli pseudo-elementi ::view-transition-group(.card) vengono nidificati all'interno dello pseudo-elemento ::view-transition-group(cards) e vengono ritagliati da quest'ultimo. Quella #targeted-card è esclusa perché la sua animazione di entrata/uscita non deve essere tagliata da ::view-transition-group(cards).

/* The .cards wrapper contains all children */
.cards {
  view-transition-name: cards;
  view-transition-group: contain;
}

/* Contents that bleed out get clipped */
&::view-transition-group-children(cards) {
  overflow: clip;
}

/* Each card is given a v-t-name and v-t-class */
.card {
  view-transition-name: match-element;
  view-transition-class: card;
}

/* The targeted card is given a unique name (to style the pseudo differently)
   and shouldn't be contained by the ::view-transition-group-children(cards) pseudo */
#targeted-card {
  view-transition-name: targeted-card;
  view-transition-group: none;
}

Riepilogo

Le transizioni di visualizzazione nidificate consentono di conservare parte della topologia dell'albero DOM durante la costruzione degli pseudo elementi. In questo modo si sbloccano una serie di effetti non possibili in precedenza con le transizioni di visualizzazione, alcuni dei quali sono descritti qui.

L'annidamento modifica il modello di costruzione delle transizioni di visualizzazione e deve essere utilizzato per creare effetti avanzati. Come indicato, anche le transizioni di visualizzazione con ambito elemento possono ottenere un sottoinsieme degli effetti con un modello più semplice. Ti invitiamo a provare entrambe le funzionalità per decidere quale soddisfa al meglio le tue esigenze.