Aspnetcore: Introduce the concept of state transitions in Blazor to allow for e.g. animations

Created on 20 May 2019  路  14Comments  路  Source: dotnet/aspnetcore

Is your feature request related to a problem? Please describe.

Following on from my StackOverflow question...

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 can be done with CSS, but I think that animating the removal of a component will be impossible because it simply doesn't exist in the next render, so there's nothing to hang the animation onto?

Describe the solution you'd like

In React there used to be a series of attributes related to state transitions, which now seem to have been replaced by react-transition-group. That looks like a big hammer for what seems like a fairly small nut.

Ideally I'd favour something really simple to use, like having an on-removal-class attribute for the name of a CSS class that's then added to the component, and allowed to complete, when the item is identified for removal from the render tree. That seems like it may be possible, because if I understand Blazor correctly the new and old render trees are diff'd to work out what parts to re-render?

There are potentially lots of other things you might want to happen in a state transition too, and no-doubt some that can't be handled with CSS alone, but I think the animation side of things is fairly important to aid users in understanding the consequences of a particular UI interaction?

affected-few area-blazor c enhancement severity-major

Most helpful comment

Here is my solution: https://github.com/dazinator/BlazorDeferredRemove
Provided a couple of helper components that can wait for a CSS property transition, or css animation to complete, before removing some content.

All 14 comments

We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

Perhaps this is implied in the original post, but another thing that would be really nice to have animated would be page transitions.

Essentially, it would be nice that as you navigate from page to page, the new pages would swoop in and out from the left, right, up or down depending on some setting. I am very new to Blazor so I am not sure if this is possible to do right now but I have a feeling it is not.

This is something that other client side frameworks such as @SteveSandersonMS's knockoutjs made very easy. I know that Blazor is still fairly premature but features like this make demo's cooler, so naturally I thought it would have been in since the first alpha! :-) I guess showing dll's written in c# and loaded into a browser was enough to sell audiences at demo's (it was enough to sell me, but see note below) but come on.. transitions on DOM content added or removed from the tree is surely a must have feature?
Oh -if anyone is able to share a link to a blazor sample app that does simple transitions of DOM content it would be useful.

Note: because now I have to sell my bosses on the blazor apps that I am writing - and they don't gasp as much when I show them the bit where the dll's are running in the browser. In fact they don't gasp at all :-( - they just look at the empty white page and wonder why when the first page finally appears it doesn't swoop in fancily from the right hand side like their old application did!

I settled on the following solution to applying a transition to an element before removal, it's not the greatest because I am using Task.Delay with a specified time in milliseconds which needs to match up o the duration of the transition specified in the css:

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;       
    }
}

@tomRedox Did you manage to get anywhere with this? I'm currently building a chart where I want items to animate between state changes. I'm currently getting around it by toggling a boolean as a parameter on each component I want to animate (quite similarly to @dazinator 's suggestion above). However, this is pretty awful, as it results in basically everything re-rendering.

I hate my "solution".. blazor needs something proper for this!

@Weezeal no I'm afraid not, from experience with React I don't think there really is anywhere to go until the framework incorporates something we can use for this.

The stack overflow question is at 600 views and 1 upvote in 4 months, so I guess there's not that much demand for this yet, but I agree with @dazinator that this is one of those things that clients and managers have got so used to seeing in apps built with other SPA frameworks that it's now slightly jarring when an app doesn't do it; it's the sort of thing that non-technical people spot.

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.

@SteveSandersonMS

, and incorporates logic to delay the removal of each item so it can be animated out.

Not sure if this was the best approach but I tried this but I don't like it because suppose you are animating a fade out transition, in your css you might specify a value such as 2 seconds to fade over. In blazor you must now task.delay the removal by atleast 2 seconds otherwise the UI element will be removed before the animation completes. So the animation timing value (2 seconds) is specified in the css, yet you now must also specify 2 seconds when using the blazor animator so it can defer removal by the right amount to tie up with the animation.et they are now If you decide to change the css to fade out over 3 seconds you now must change the corresponding blazor delay value to 3.
It works, and is the approach I an using for lack of any other, but there has to be a better solution, onee that makes better use of events in the DOM to work out when an animation has completed so removal can be deferred until that event is received,. That way you wouldn't need to specify any delay value like 2 or 3 seconds etc on the blazor side and animation timings can be left purely managed in css

Yes, you can implement any strategy you want for deciding how long the delay should be. It could delay until some DOM event occurs.

Ok thanks. I'll try and revisit this at some point then and see if I can get past that final hurdle. If it works out I'll open source something and post a link here.

Here is my solution: https://github.com/dazinator/BlazorDeferredRemove
Provided a couple of helper components that can wait for a CSS property transition, or css animation to complete, before removing some content.

This is my number one blocker for adopting blazor. I need to know that I can keep my application beautiful with intro, outro, update, and list-move transition animations. I really enjoy the solution that Vue.JS came up with. It solves all my perceived use cases in a very non-obtrusive way. The solution is much like Blazor.Animate in that we wrap the html in a component.

https://github.com/NVentimiglia/Blazor.Transitions

https://vuejs.org/v2/guide/transitions.html

https://github.com/mikoskinen/Blazor.Animate/issues/10

Was this page helpful?
0 / 5 - 0 ratings