Skip to content

Instantly share code, notes, and snippets.

@noamr
Last active November 22, 2023 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noamr/3603b9fc0264412731021b962d9bbedf to your computer and use it in GitHub Desktop.
Save noamr/3603b9fc0264412731021b962d9bbedf to your computer and use it in GitHub Desktop.
View Transition Types example

VT Use case for types

Music App

Going back to the original use case for types. We have a music app with 4 pages:

  • Home
  • Playlist
  • What's new
  • Song

The following transitions are designed:

  • Switching between home/playlist/what's new should have a transition where the whole page slides to the left.
  • Going "back" should slide to the right instead.
  • Moving from song to/from home should fade the song in/out
  • Moving from playlist to/from song should expand the song thumbnail to the full artwork hero.
  • Reordering the playlist has its own transition.

So we define several transition types:

  • slide-left
  • slide-right
  • song-fade
  • song-expand
  • list-reorder

The page itself has an class on the HTML element for which page this is (.home, .playlist, .song).

To do this in SPA:

function resolveTransitionType(currentURL, newURL, navigationType) {
    if ((currentURL.pathname === "/playlist" && newURL.pathname === "/song")
        || (currentURL.pathname === "/song" && newURL.pathname === "/playlist"))
        return "song-expand";
    else if (currentURL.pathname === "/song" || newURL.pathname === "/song")
        return "song-fade";
    else if (navigationType === "back")
        return "slide-right";
    else
        return "slide-left";

}
function navigateWithTransition(url, navigationType) {
    document.startViewTransition({
        update: () => navigate(newURL, navigationType),
        types: [resolveTransitionType(new URL(location.href), url, navigationType)]
    });
}

function reorderList() {
    startViewTransition({update: () => sortList(), types: ["list-reorder"]});
}
html:active-view-transition(song-expand) {
    &.song img.artwork { view-transition-name: song }
    &.playlist li.item.current img.artwork { view-transition-name: song }
}

html:active-view-transition(song-fade) {
    &.song article.song { view-transition-name: article }
}

html:active-view-transition(slide-left, slide-right) {
    ::view-transition-group(*) {
        animation-name: slide;
    }

    &:active-view-transition(slide-right) {
        animation-direction: reverse;
    }
}

To do this in MPA, put this in all pages:

@view-transition {
    navigation: auto;
    type: slide-left;
}

@view-transition {
    navigation: back;
    type: slide-right;
}

@view-transition {
    navigation: auto;
    from: "/song";
    type: song-fade;
}

@view-transition {
    navigation: auto;
    to: "/song";
    type: song-fade;
}

@view-transition {
    navigation: auto;
    from: "/playlist";
    to: "/song";
    type: song-expand;
}

@view-transition {
    navigation: auto;
    from: "/song";
    to: "/playlist";
    type: song-expand;
}

Without from / to, but with mutable types and events:

@view-transition {
    navigation: auto;
}
function resolveTransitionType(oldURL, newURL, navigationType) {
// same as in SPA
}

window.addEventListener("reveal", event => {
    if (event.viewTransition && navigation.activation.from)
        event.viewTransition.types = [
            resolveTransitionType(navigation.activation.from.url,
                navigation.activation.entry.url,
                navigation.activation.type)];
});

window.addEventListener("outboundviewtransition", event => {
    event.types = [resolveTransitionType(new URL(location.href), new URL(event.url), event.navigationType)]
})

fallback

The use-case: a page where elements transition to each other, but only if they're loaded. If the page is only partially loaded without those elements, the whole root elements transitions with a fade.

Old document:

@view-transition {
    navigation: auto;
    type: morph;
}

::active-view-transition(morph) {
    #some-element-1 {view-transition-name: foobar}
}

New document:

<style>
@view-transition {
    navigation: auto;
    type: fallback;
}

::active-view-transition(fallback) {
    ::view-transition-group(*) {
        animation: none;
    }

    ::view-transition-group(root) {
        animation-name: fade;
    }
}

::active-view-transition(morph) {
    .some-element-2 {view-transition-name: foobar}
}
</style>
<!-- a whole bunch of content, progressively loaded-->
<div class="some-element-2">stuff</div>
<style>
@view-transition {
    navigation: auto;
    type: morph;
}
</style>

landing page

We have an app that has a simple slide between pages, and a can be accessed from a landing page. If accessed from the landing page it should morph the hero image instead of slide.

Landing page:

img.hero { view-transition-name: hero }
@view-transition { navigation: auto }

App:

@view-transition {
    navigation: auto;
    type: entry;
}

html:active-view-transition(slide) {
    ::view-transition-group(*) { animation-name: slide }
}

html:active-view-transition(entry) {
    ::view-transition-group(*) { animation-name: fade }
}

If the app itself is an MPA:

@view-transition {
    navigation: auto;
    type: slide;
}

If we have from:

@view-transition {
    navigation: auto;
    from: "/landing";
    type: entry;
}

Or if we don't have from:

window.addEventListener("pagereveal", ({viewTransition}) => {
    if (viewTransition && navigation.activation?.from === "/landing")
        viewTransition?.type = "entry";
})

"Full app" vs "effect"

We have an app where the whole app slides way when switching between pages, but popups expand in while the rest of the page blurs and zooms out. Note that the whole page participates in the popup transition, it's not exactly a "scoped element transition".

@view-transition {
    navigation: auto;
    type: slide;
}

html:active-view-transition(slide) {
    ::view-transition-group(root) { animation-name: slide }
}

html:active-view-transition(expand-popup) {
    ::view-transition-group(root) { animation-name: zoom-out }
    ::view-transition-group(popup) { animaiton-name: expand }
}
// If an SPA
function switchPage(url) {
    document.startViewTransition({update: router.switchPage(url), types: ["slide"]})
}

function showPopup() {
    document.startViewTransition({update: () => {...}, types: ["expand-popup"]})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment