You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SignalR的StateHasChanged事件触发时启动CSS动画

实现数据更新时触发行CSS动画

核心思路

通过给更新后的行临时添加动画CSS类,待动画执行完成后移除该类,确保每次数据更新都能触发一次动画效果。以下是具体实现步骤:


1. 定义动画CSS样式

假设你已有鼠标悬停的动画样式,新增一个专门用于数据更新触发的动画类(也可复用现有动画逻辑,仅调整触发方式):

/* 你的现有悬停动画 */
.row-hover {
  transition: background-color 0.3s ease;
}

/* 数据更新触发的动画 */
.row-update-animate {
  animation: rowFlash 0.8s ease-out forwards;
}

/* 动画关键帧 */
@keyframes rowFlash {
  0% { background-color: #fff3cd; }
  100% { background-color: transparent; }
}

2. 组件内跟踪更新的行

在Blazor组件中,用集合记录刚更新的行ID,用于渲染时动态添加动画类:

private List<WaitTimeItem> WaitTimeList { get; set; } = new();
// 存储需要触发动画的行ID
private HashSet<int> UpdatedRowIds { get; set; } = new();

// SignalR接收数据更新的方法
private async Task ReceiveWaitTimeUpdate(WaitTimeItem updatedItem)
{
    var targetItem = WaitTimeList.FirstOrDefault(item => item.Id == updatedItem.Id);
    if (targetItem != null)
    {
        // 更新数据
        targetItem.WaitDuration = updatedItem.WaitDuration;
        // 标记该行需要动画
        UpdatedRowIds.Add(updatedItem.Id);
        // 触发UI刷新
        StateHasChanged();

        // 延迟移除标记(时长匹配动画时长)
        await Task.Delay(800);
        UpdatedRowIds.Remove(updatedItem.Id);
        StateHasChanged();
    }
}

3. 渲染行时动态绑定类

在循环渲染表格行的位置,根据UpdatedRowIds判断是否添加动画类:

<table class="table">
    <tbody>
        @foreach (var item in WaitTimeList)
        {
            <tr id="row-@item.Id" class="@GetRowCssClass(item.Id)">
                <td>@item.ServiceName</td>
                <td>@item.WaitDuration 分钟</td>
            </tr>
        }
    </tbody>
</table>

@code {
    private string GetRowCssClass(int itemId)
    {
        var baseClasses = "row-hover";
        if (UpdatedRowIds.Contains(itemId))
        {
            baseClasses += " row-update-animate";
        }
        return baseClasses;
    }
}

进阶方案:用JS监听动画结束(更精准)

如果需要更灵活的动画控制(比如动画结束后立即移除类,无需固定延迟),可以通过JS互操作实现:

步骤1:注入IJSRuntime

[Inject]
private IJSRuntime JSRuntime { get; set; }

步骤2:修改数据更新方法

private async Task ReceiveWaitTimeUpdate(WaitTimeItem updatedItem)
{
    var targetItem = WaitTimeList.FirstOrDefault(item => item.Id == updatedItem.Id);
    if (targetItem != null)
    {
        targetItem.WaitDuration = updatedItem.WaitDuration;
        UpdatedRowIds.Add(updatedItem.Id);
        StateHasChanged();

        // 调用JS监听动画结束事件
        await JSRuntime.InvokeVoidAsync("watchAnimationEnd", $"row-{updatedItem.Id}", updatedItem.Id);
    }
}

步骤3:添加JS逻辑(wwwroot/js/site.js)

window.watchAnimationEnd = function(rowElementId, itemId) {
    const row = document.getElementById(rowElementId);
    if (!row) return;

    function onAnimationEnd() {
        row.classList.remove('row-update-animate');
        row.removeEventListener('animationend', onAnimationEnd);
        // 通知Blazor移除更新标记
        DotNet.invokeMethodAsync('YourProjectAssemblyName', 'ClearUpdatedRowId', itemId);
    }

    row.addEventListener('animationend', onAnimationEnd);
}

步骤4:组件中添加可调用方法

[JSInvokable]
public void ClearUpdatedRowId(int itemId)
{
    UpdatedRowIds.Remove(itemId);
    StateHasChanged();
}

注意事项

  • 动画时长要和延迟时间(或JS监听的动画结束事件)匹配,避免动画中断
  • 若使用Blazor Server,确保SignalR回调在正确的同步上下文执行(默认已处理,无需额外操作)
  • 动画类不要设置为永久生效,否则无法重复触发

内容的提问来源于stack exchange,提问作者Mike Murphy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:10:31