Skip to content

Instantly share code, notes, and snippets.

@noamr
Last active November 11, 2024 13:25
Show Gist options
  • 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

!!DEPRECATED!!

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"]})
}
@slava-uxd
Copy link

Found this via search, so I may miss some context. But the syntax @view-transition ( type: ) doesn't seem to work at all on MPA.

"types:" does work, but not singular "type:"

Also "from:" and "to:" do not. What's going on here? (Chrome 130)

@noamr
Copy link
Author

noamr commented Nov 11, 2024

Found this via search, so I may miss some context. But the syntax @view-transition ( type: ) doesn't seem to work at all on MPA.

"types:" does work, but not singular "type:"

Also "from:" and "to:" do not. What's going on here? (Chrome 130)

This gist was prepared for spec discussions a while ago as an example, and it's different from what ended up landing in the spec/implementation. It's not in mdn yet, see backlog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment