【问题标题】:How to animate state transitions in Blazor?如何在 Blazor 中为状态转换设置动画?
【发布时间】:2019-10-06 18:25:26
【问题描述】:

在我的 Blazor 组件中,我经常根据条件语句渲染组件,例如

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

或来自循环,例如

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

当我更改状态时,我想为状态转换设置动画,以便组件淡入/淡出。在上面的示例中,当IsCustomer 更改,或者在SalesOrders 集合中添加或删除记录时,可能会发生这种情况。

动画添加组件

我可以看到在添加组件时如何实现这一点,通过具有 CSS 类的组件在组件呈现时发生淡入动画 - 例如如图Chris Sainty's excellent toast example

动画移除组件

我想不出你在移除一个组件时会怎么做,因为当 DOM 的那部分重新渲染时,该组件就会停止存在?

React 有 react-transition-group 处理过渡期,但我在 Blazor 中找不到类似的东西吗?

有没有办法在 Blazor 中为移除组件添加动画?

动画页面过渡

另一个经常动画的状态转换是改变“页面”。再说一次,我目前在 Blazor 中找不到这样做的方法吗?实际上,这可能只是一个用于删除旧页面组件的动画,而另一个用于添加新页面组件的动画,但在某些框架中是在路由级别而不是组件级别完成的 - 我目前在 Blazor 的任何一个级别都找不到任何东西?

【问题讨论】:

  • 一种解决方法是让组件保持活动状态,但将 if 条件置于 CSS 可见性、宽度或高度以显示或隐藏它。
  • @Sorush 这是一个非常有用的想法 - 为什么不通过一个小例子将其添加为答案?也有可能在blazorfiddle.com 上运行它
  • 我发布了一个新答案。

标签: c# blazor


【解决方案1】:

Blazor 没有涵盖这种情况,为此,您需要使用 CSS。很难给你一个具体的例子,因为它完全取决于你希望你的动画如何工作以及使用什么样的样式,但我建议检查 CSS 过渡和关键帧。

这里有一些很好的资源

正如您在问题处理项目中提到的那样,我还无法弄清楚。所以很遗憾,我无能为力。

【讨论】:

  • 谢谢克里斯,很高兴不仅仅是我!我需要在某个时候给你买杯啤酒,你的 cmets 和博客真的帮助我提高了 Blazor 的学习曲线!
【解决方案2】:

对于组件移除方面的事情:

我在 AspNetCore 存储库中添加了一个 GitHub feature request 用于删除。 Microsoft 的 Steve Sanderson 概述了使用 Blazor 框架公开的 API 应该已经可以删除动画组件,但它可能会受益于编写一个包以使其更易于实现:

我期望的解决方案是创建一个动画组件 呈现列表或单个项目,并将逻辑合并到 延迟删除每个项目,以便将其动画化。布雷泽 模板组件已经有了很好的原语,所以结果 API 应该很不错。这基本上是一样的 其他 SPA 框架中使用的解决方案。

这可以在用户代码中实现,不需要内置 框架功能。我不是说这很容易,但希望有人在 社区会抽出时间来做这件事。这是我可以自己做的事情 在某个时候,但在短期内有其他优先事项。

dazinator / @Darrell 接受了这一点,并制作了 Blazor Deferred Remove Nuget 包来做到这一点。我还没有尝试过,但看起来需要什么才能实现这一点。

现在还有Blazor.Animate package

【讨论】:

    【解决方案3】:

    更新:我没有使用下面的方法,而是在 GitHub 上提供了一个改进的解决方案(不依赖于 Task.Delay):https://github.com/dazinator/BlazorDeferredRemove

    我决定采用以下方法在元素被 blazor 从 DOM 中删除之前处理​​它的淡出。这是一种使用 Task.Delay 的解决方法,折衷方案是因为我使用 Task.Delay 的指定时间(以毫秒为单位),所以此时间需要与您在 css 中用于过渡的持续时间保持一致 - 否则元素可能会被 blazor 删除在过渡完成之前(或之后):

    可重用的 Transition.razor 组件:

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

    它在父页面或组件中被这样使用:

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

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

    【讨论】:

      【解决方案4】:

      解决方法

      我们可以使用 CSS 显示/隐藏。只需确保处理隐藏时传递给组件的 null 参数即可。

      假设我们有组件Person,如下所示:

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

      我们在另一个页面/组件中使用它,如下所示:

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

      我在 CSS 中使用了@width@height@AnimTime 变量来表明可能性是无穷无尽的。 我应该注意,Blazor 组件中的 keyframes 应该以符号 (@@) 的双精度开头。

      BlazorFiddle 上查看现场演示,在JSFiddle 上查看 html+CSS。

      另一个技巧

      如果用户点击像closeX这样的按钮,你可以在鼠标按下到鼠标的时间内挤压一个0.1s或0.2s的短动画-向上。鼠标按下触发动画,但鼠标向上删除组件。我在here 之前编写了这个代码

      【讨论】:

        【解决方案5】:

        关于这个问题的动画页面过渡部分...

        blazor 中的转换并非直截了当。当需要时,您必须考虑重置状态和保留状态模式,例如当返回到先前的路线并且需要保留滚动位置时。使用 css 的过渡动画是一个很好的方法。有一个解决方案允许使用您选择的任何转换方法(此处的答案中的其他地方提供了示例)。

        我建议您查看这个 nuget 包和 github 存储库,它可以解决这个确切的问题。

        https://github.com/JByfordRew/BlazorTransitionableRoute

        【讨论】:

          猜你喜欢
          • 2018-12-18
          • 2020-04-18
          • 1970-01-01
          • 2011-07-01
          • 2015-09-12
          • 1970-01-01
          • 2013-01-09
          • 2014-02-07
          • 1970-01-01
          相关资源
          最近更新 更多