• Blazor
  • ASP.NET Core
  • .NET
  • C#

Blazor - Page Animated Transitions

With this article I will go over how to use BlazorTransitionableRoute to create an animated transition between Blazor page transitions.

When playing around with my canhorn/EventHorizon.Blazor.UX project I wanted to add a bit of movement to every action the user does, and the most up front way to give this impression is during page navigation. I use the BlazorTransitionableRoute package with my Blazor application to help control the timing of page transitions, below I include a hight over view of the code I used, it's not a working example here, but you can checkout canhorn/EventHorizon.Blazor.UX for the source code, and the EventHorizon.Blazor.UX sample site for a live demo.

Example

Checkout this awesome Blazor package that I used to take care of the plumbing of hooking into the Route events: BlazorTransitionableRoute

To handle the heavy lifting of the transition animations this solution uses Animate.css.

Checkout a live Demo here EventHorizon.Blazor.UX.

You can also checkout the source of the whole site in the canhorn/EventHorizon.Blazor.UX Github repository.

App.razor
Here is the LayoutView, with two new routes. The routes are the current and next route.

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <LayoutView Layout="@typeof(MainLayout)">
            <TransitionableRoutePrimary RouteData="@routeData">
                <TransitionableRouteView DefaultLayout="@typeof(MyViewLayout)" />
            </TransitionableRoutePrimary>
            <TransitionableRouteSecondary RouteData="@routeData">
                <TransitionableRouteView DefaultLayout="@typeof(MyViewLayout)" />
            </TransitionableRouteSecondary>
        </LayoutView>
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

MyViewLayout.razor
This custom View Layout, when used with the App.razor above, helps with transition and timing of all the pages content.

@inherits TransitionableLayoutComponent

<div class="view-content transition @transitioningClass">
    @Body
</div>

@code {
    // Triggers the transition in/out of this view
    private string transitioningClass => TransitioningIn ? "transition-in transitioning" : "transition-out transitioning";
}

MyViewLayout.razor.css
Helps to style the page content for a smoother transition.

.view-content {
    padding-left: 2rem !important;
    padding-right: 1.5rem !important;
}

.transition-in {
    opacity: 0;
}

.transitioning {
    position: absolute;
    left: 0;
    right: 0;
}

.transition-display-none {
    display: none;
}

JavaScript
Slightly complicated but handles the in/out animation of the transition marked elements on the page and other edge cases.

window.ehzBlazorUx = {
    transitionFunction: function (back) {

        let transitionIn = document.getElementsByClassName('transition-in')[0];
        let transitionOut = document.getElementsByClassName('transition-out')[0];

        if (transitionIn && transitionOut) {
            const handle_transitionOut_onanimationend = () => {
                console.log('Transition ended');
                transitionOut.removeEventListener("animationend", handle_transitionOut_onanimationend);
                transitionOut.classList.remove("transitioning");
                if (transitionOut.classList.contains(
                    "transition__out"
                )) {
                    transitionOut.classList.add('transition-display-none');
                }
                if (transitionOut.classList.contains(
                    "transition__in"
                )) {
                    transitionOut.classList.remove('transition-display-none');
                }
            };
            const handle_transitionOut_onanimationcancel = () => {
                console.log('Animation Cancel');
                transitionOut.removeEventListener("animationend", handle_transitionOut_onanimationend);
                transitionOut.removeEventListener("animationcancel", handle_transitionOut_onanimationcancel);
            };
            transitionOut.addEventListener('animationend', handle_transitionOut_onanimationend);
            transitionOut.addEventListener('animationcancel', handle_transitionOut_onanimationcancel);

            transitionOut.classList.remove('transition-out');
            transitionOut.classList.remove('transition-display-none');
            transitionOut.classList.add(
                "transition__out",
                // These are Animate.css provided css
                "animate__zoomOut",
                "animate__slow",
                "animate__animated"
            );

            const handle_transitionIn_onanimationend = () => {
                console.log('Animation ended');
                transitionIn.removeEventListener("animationend", handle_transitionIn_onanimationend);
                if (transitionIn.classList.contains(
                    "transition__in"
                )) {
                    transitionIn.classList.remove('transition-display-none');
                }
                if (transitionIn.classList.contains(
                    "transition__out"
                )) {
                    transitionIn.classList.add('transition-display-none');
                }
            };
            const handle_transitionIn_onanimationcancel = () => {
                console.log('Animation Cancel');
                transitionIn.removeEventListener("animationend", handle_transitionIn_onanimationend);
                transitionIn.removeEventListener("animationcancel", handle_transitionIn_onanimationcancel);
            };
            transitionIn.addEventListener('animationend', handle_transitionIn_onanimationend);
            transitionIn.addEventListener('animationcancel', handle_transitionIn_onanimationcancel);

            transitionIn.classList.remove('transition-in');
            transitionIn.classList.add(
                "transition__in",
                // These are Animate.css provided css
                "animate__zoomIn",
                "animate__animated"
            );
        }
    }
}
Cody's logo image, it is an abstract of a black hole with a white Event Horizon.

Cody Merritt Anhorn

A Engineer with a passion for Platform Architecture and Tool Development.