26
votes

In my Blazor components I often render components based on either a conditional statement, e.g.

@if (_contact.IsCustomer)
{
    <SalesOrdersList Customer="@_contact" />
}

Or from a loop, e.g.

@foreach(var salesOrder in _customer.SalesOrders)
{
    <SalesOrderSummary SalesOrder="@salesOrder" />
}

When I change the state I'd like to animate the state transition so that the components fade in/out. In the examples above that might happen when IsCustomer changes, or when a record is added or removed from the SalesOrders collection.

Animating adding a component

I can see how this can be achieved when adding a component, by the component having a CSS class that has a fade in animation that occurs when the component renders - e.g. as shown in Chris Sainty's excellent toast example

Animating removing a component

I can't think how you would do that when removing a component, because the component simply stops existing when that part of the DOM re-renders?

React has react-transition-group that deals with the period of transition, but I can't find anything similar in Blazor as of yet?

Is there any way to do add an animation for the removal of a component in Blazor?

Animating Page Transitions

The other often-animated state-transition is changing 'pages'. Again, I can't find way to do that in Blazor at present? Effectively that can just be an animation for removing the old page component and another for adding the new one, but in some frameworks that's done at the routing level rather than the component level - I can't find anything at either level in Blazor at present?

5
one workaround is to keep the component alive but put if-condition on CSS visibility, width or height to show or hide it. - Sorush
@Sorush that's a really useful idea - why not add it as an answer with a small example? It might be possible to get it running on here blazorfiddle.com too - tomRedox
I posted a new answer. - Sorush

5 Answers

8
votes

Blazor doesn't cover this scenario, for this, you would need to use CSS. It's hard to give you a specific example as it depends on exactly how you want your animations to work and with what kind of style but I would suggest checking out CSS transitions and keyframes.

Here are some good resources

As you mentioned in your question handling items being removed is something I've not been able to figure out just yet. So I can't help with that unfortunately.

5
votes

Update: Rather than use the below, I've put an improved solution for this (doesn't rely on Task.Delay) on GitHub: https://github.com/dazinator/BlazorDeferredRemove

I settled on the following approach for handling fading out an element before it gets removed by blazor from the DOM. It's a workaround that uses Task.Delay and the compromise is that because I am using Task.Delay with a specified time in milliseconds this time needs to align with the duration you use in css for the transition - otherwise the element might be removed by blazor before (or after) the transition has completes:

Reusable Transition.razor component:

    <div class="@(ToggleActive ? ToggleTransitionOnCssClassName: ToggleTransitionOffCssClassName)">
        @ChildContent;
    </div>

    @code {

    [Parameter] RenderFragment ChildContent { get; set; }

    [Parameter] string ToggleTransitionOnCssClassName { get; set; } = "";
    [Parameter] string ToggleTransitionOffCssClassName { get; set; } = "";

    [Parameter] int TransitionDurationMilliseconds { get; set; } = 200;

    public bool ToggleActive { get; set; }

    [Parameter] EventCallback<bool> TransitionEnded { get; set; }

    public async Task ToggleTransition()
    {
        ToggleActive = !ToggleActive;
        await Task.Delay(TransitionDurationMilliseconds);
        await TransitionEnded.InvokeAsync(ToggleActive);
    }


    }

and it's used like so from a parent page or component:

         @if (RenderThingy)
        {
            <Transition @ref="Transition" TransitionDurationMilliseconds="500" ToggleTransitionOnCssClassName="m-fadeOut" ToggleTransitionOffCssClassName="m-fadeIn" TransitionEnded="@TransitionComplete">
                <RenderThingy OnDismissed="@OnDismissed"></RenderThingy>
            </Transition>
        }

@code {

    Transition Transition { get; set; }
    bool RenderThingy {get; set;} = true;

    async Task OnDismissed()
    {
        await Transition.ToggleTransition();
    }
    private void TransitionComplete(bool toggleState)
    {
        RenderThingy = false;
    }
}

and css:

.m-fadeIn {
    visibility: visible;
    opacity: 1;
    animation: fadein 500ms;
}

@keyframes fadein {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

.m-fadeOut {
    animation: fadeout 500ms;
}

@keyframes fadeout {
    from {
        opacity: 1;
    }

    to {
        opacity: 0;       
    }
}
5
votes

For the component removal side of things:

I added a GitHub feature request for removing on the AspNetCore repository. Microsoft's Steve Sanderson has outlined that animating component removal should already be possible with the API the Blazor framework exposes, but it would probably benefit from someone writing a package to make it simpler to implement:

The solution I'd expect is to create an animator component that renders either a list or a single item, and incorporates logic to delay the removal of each item so it can be animated out. Blazor already has good primitives for templates components, so the resulting APIs should be pretty nice. This would be essentially the same solution that is used in other SPA frameworks.

This is achievable in user code and doesn't require a built-in framework feature. I'm not saying it's easy, but hopefully someone in the community will find time to do it. It's something I may do myself at some point but have other priorities in the short term.

dazinator / @Darrell has taken that on and produced the Blazor Deferred Remove Nuget package to do that. I haven't tried that yet, but it looks like what's needed to achieve this.

There's now also the Blazor.Animate package.

3
votes

A workaround

we can show/hide using CSS. Just make sure handle null parameters that are passed to the component when it's hidden.

Let say we have component Person like below:

<style>
.container{
    display:inline-block;
    width:@width;
    height:@height;
    color:white;
    background-color:blue;
}
.fade-out{ 
   animation: fade @AnimTime linear forwards;
}
.spin-in{
   animation: spin @AnimTime linear forwards;
}

@@keyframes fade {
  0%   {opacity:1;}
  99%  {width:@width; height:@height;}
  100% {width:0px;height:0px; opacity:0;}
}
@@keyframes spin {
  0%   {width:0px;height:0px; transform: rotate(0deg);}
  99%  {width:@width; height:@height;}
  100% {width:@width;height:@height;transform: rotate(1440deg);}
}
</style>
<div class="container @(IsShown?"spin-in":"fade-out")">
    <span>@(Name??"")</span>
</div>
@code {
    string width="100px";
    string height="20px";

    [Parameter]
    public string Name { get; set; }

    [Parameter]
    public string AnimTime { get; set; } 

    [Parameter]
    public bool IsShown {get; set;}
}

and we use it in another page/component like below:

<button @onclick="(_=>{isShown=!isShown;})">Switch</button>
<span>Left hand of component</span>
<Person Name="Jack" AnimTime="2s" IsShown=@isShown/>
<span>Right hand of component</span>
@code{
    bool isShown=true;
}

I used @width, @height, @AnimTime variables in CSS to just show possibilities are endless. I should note that keyframes in Blazor component should start with double at symbol (@@).

See the live demo on BlazorFiddle and html+CSS only on JSFiddle.

Another trick

In the case that user clicks on a button like close or X you can squeeze a 0.1s or 0.2s short animation in the period of mouse-down to mouse-up. Mouse-down triggers animation but mouse up remove the component. I coded this before here

0
votes

In relation to Animating Page Transition part of this question ...

Transitions in blazor is not straight forward. You have to consider resetting state and preserving state modes when this is a requirement for instance when moving back to a previous route and need scroll position to be preserved. Transition animations using css are a good way to do this. There is a solution that allows using any transition method you choose (where examples are provided elsewhere in the answers here).

I would recommend you looking at this nuget package and github repository which exists to solve this exact issue.

https://github.com/JByfordRew/BlazorTransitionableRoute