如何在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
相关产品推荐
相关产品推荐

