MudAnimate - Impossible to set concatenate animation
I have to animate a bar in order to apper and disappear using a bool property. When this property is set to true the bar must appear, when false must disappear.
In first place I've tried to use two identical MudAnimate with AnimationDirection opposite (one set on Normal and other on Reverse), but i've also tried to set it to alternate but it doesn't seem to work.
Any idea?
Changing reverse and refreshing animation should work i think. Do you have example code?
I'm trying to solve this at the moment, once the animation is complete it resets to it's initial position. The most awkward part is trying to catch it at the end of the animation to pause it, because if you don't pause it, it just resets to the starting position.
So far I've got it so it unpauses, refreshes the animation, waits for the amount that the duration is, then set the direction, pause, do StateHasChanged() otherwise it doesn't care that you set Pause to true, then refresh again.
When it's doing that for it to pause at the start of the reverse direction it flashes into it's default position for like a millisecond. Which is the current issue I'm trying to mitigate.
The fact it resets after the animation seems to be something that is maybe added by this library because I looked into Animista which I think is what you used? but in their playground the equivelant animation pauses and stays at its end position. Is there a possibility of getting that same behaviour through this, whether it be the default behaviour or just an option?
At a quick glance is it potentially a side effect of the following where it's flipping the state of _true in the refresh method? at least I think the flashing when refreshing a reverse direction might be.
bool _show = true;
public async Task Refresh()
{
_show = false;
await Task.Delay(10);
await InvokeAsync(StateHasChanged);
_show = true;
await Task.Delay(10);
await InvokeAsync(StateHasChanged);
}
@(Selector)@(Hover ? ":hover" : null) {
@if (_show == true)
{
@($"animation: {AnimationName} {Duration.ToInvariantString()}s {AnimationTiming.ToDescriptionString()} {(Delay != 0 ? Delay.ToInvariantString() + "s" : null)} {(Infinite ? "infinite" : IterationCount.ToString())} {AnimationDirection.ToDescriptionString()} {(Paused ? "paused" : null)} {AnimationFillMode.ToDescriptionString()};");
}
else
{
@("animation: none;");
}
}
@MrScottyTay you can set AnimationDirection and AnimationFillMode
So now I've got it set to Direction Normal and Fill Forwards for moving upwards out of the way with a negative value. Then for it to reappear I change the Direction to Reverse and Fill Backwards.
works fine for the forwards but but the backwards still flickers for like a single frame into it's final (original) position before sliding back down from above.
I trigger the following when a bool changes signifying if the element should be hidden away or visible at the end of the animation:
private async Task TriggerAnimationAsync()
{
Direction = State ? AnimationDirection.Normal : AnimationDirection.Reverse;
FillMode = State ? AnimationFillMode.Forwards : AnimationFillMode.Backwards;
await _animate.Refresh();
}
I'm suspecting that the components Refresh code is setting the element to have no animation for 10ms which is making it flicker into it's original position whenever refresh is called before doing the animation.
@mckaragoz as @MrScottyTay mentioned the "single frame show" problem truly exists:
https://github.com/user-attachments/assets/e9601ba0-f34f-4457-8309-115557a2698c
The video has less than 60 fps but if you see it in 60 fps then it looks much worse. The single frame problem shows every time after changing the Direction property. Is it a bug or we can avoid it somehow?
I was also thinking about pausing the animation set as infinite but there will be a lot of logic behind
Well, im not sure also, we can suspect 10ms delay. We bring the delay because we have inconsistent results when we quickly repeat animation. If it's only about 10ms delay we can add a parameter to control the delay time.
And, if it's about only show and hide a component, why don't you wrap it in a if block?
Wrapping it in an if block would just normally make the element wanting to hide to disappear instantly. The reason to use animations such as these is to make it look smooth and replace those if blocks.
The biggest issue is that this single frame flash will affect ALL animations that have fill backwards and reverse direction at the moment. Not just for our current use case because it always flashes where the element is located in the DOM itself before starting the animation.
I was playing with https://mudextensions.codebeam.org/mudanimate and figured out that:
- changing Direction from normal to reverse does not trigger reset on the object, while you have set Hover value on true;
- setting Hover from true to false triggers smooth animation.
Due to that fact I tried to made my custom component with events to set Hover value.
https://github.com/user-attachments/assets/10a6caaa-41fa-40a1-a014-a82b91e69819
It's not like the best animation you ever seen, but I don't have more time to spend on it
@using MudBlazor
@using MudExtensions
<MudCard Style="overflow-y: auto;min-height:200px; min-width: 300px; background-color: transparent; margin-bottom: 2px; scrollbar-width: none;" @onmouseenter="OnMouseEnter" @onmouseleave="OnMouseLeave">
<MudAnimate @ref="_animate"
Selector=@_selectorForAnimation
Duration="0.5"
AnimationType="AnimationType.SlideY"
Value="-170"
AnimationTiming="AnimationTiming.Ease"
AnimationFillMode="AnimationFillMode.Both"
Hover=@_isAnimationHovering
AnimationDirection=@_animationDirection />
<div class=@_selectorForElements style="height: 400px; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<MudCardHeader Style="padding-top: 95px;">
<CardHeaderContent>
<MudText Typo="Typo.h6" Color="Color.Info">@HeaderText</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent Style="padding-top: 95px; margin-bottom: 100px;">
<MudText Typo="Typo.body1">@ContentText</MudText>
</MudCardContent>
</div>
</MudCard>
@code {
private MudAnimate _animate = new();
private bool _isAnimationHovering = true;
private AnimationDirection _animationDirection = AnimationDirection.Normal;
private string _selectorForElements => SelectorName[0] == '.' ? SelectorName.Remove(0, 1) : SelectorName;
private string _selectorForAnimation => SelectorName[0] == '.' ? SelectorName : SelectorName.Insert(0, ".");
[Parameter] public string HeaderText { get; set; } = string.Empty;
[Parameter] public string ContentText { get; set; } = string.Empty;
[Parameter] public string SelectorName { get; set; } = ".animation";
private async Task OnMouseEnter()
{
_animationDirection = AnimationDirection.Normal;
_isAnimationHovering = true;
await _animate.Refresh();
}
private void OnMouseLeave()
{
_animationDirection = AnimationDirection.Reverse;
_isAnimationHovering = false;
}
}
You can use it if you want. Keep in mind that every element must use different class name
@mckaragoz btw I'm really curious, how did you make the animation of the "Secondary" paragraph on the homepage of https://mudextensions.codebeam.org? I mean the one with .slogan2 class.
For the delay, we added RefreshSensitivity parameter. that you can set any value includes 0 (no delay).
@kacperpasnik it's a basic letter-spacing change on hover: https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions/blob/8cbbb3099d411a4ae38d9e7c031fa9afd0ed62f2/CodeBeam.MudBlazor.Extensions.Docs/Pages/Index.razor#L182
For the delay, we added
RefreshSensitivityparameter. that you can set any value includes 0 (no delay).
Awesome, I look forward to experimenting with it when I get the chance. Thanks for this.
May I ask what the number represents? is it milliseconds specifically?
May I ask what the number represents? is it milliseconds specifically?
Yes. The reason why we added the delay is, it doesn't work properly when you call refresh too frequently. So we can set it to 0 for the smoothest results