如何用HTML和CSS实现Blazor仪表数字从下到上的切换动画?
Blazor Hybrid仪表数字切换动画问题
我正在开发Blazor Hybrid(Web App)项目,其中包含一个仪表显示组件,用来展示类似真实仪表的数字字符串。想要实现数字切换时从下到上的滚动动画,模拟真实仪表的动效,但目前数字是直接切换,没有任何动画。
当前无动画效果:
目标从下到上动画效果:
我尝试创建了slide-up关键帧,让新数字从下方平移至上方,同时给容器设置了overflow:hidden,但调试发现动画完全没生效。想问问我的实现方式哪里错了?
我的代码
CurrentMeterDisplayContainer.razor
@rendermode InteractiveAuto @implements IDisposable @using System.Threading <div class="meter-container"> <div class="meter-reading"> @for (int i = 0; i < meterReading.CurrentValue.Length; i++) { var digit = meterReading.CurrentValue[i]; <div class="digit-container"> <div class="digit-slide-up"> <span class="@(i >= meterReading.CurrentValue.Length - 2 ? "digit last-digits" : "digit")">@digit</span> </div> </div> } </div> </div> @code { private CurrentMeterReadings meterReading = new CurrentMeterReadings(); private Timer timer; protected override void OnInitialized() { meterReading.UpdateReading(); timer = new Timer(_ => UpdateMeter(), null, 0, 5000); } private void UpdateMeter() { meterReading.UpdateReading(); InvokeAsync(StateHasChanged); } public void Dispose() { timer?.Dispose(); } }
CurrentMeterDisplayContainer.razor.css
.meter-container { padding: 2px; /* height: 100%; */ background-color: #fff; border-radius: 5px; } .meter-reading { display: flex; } .digit-container { /* padding: 1px; */ margin: 2px; background-color: #fff; border-radius: 5px; width: 22px; height: 40px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: flex; justify-content: center; align-items: center; overflow: hidden; } .digit { font-family: "Bebas Regular"; font-size: 1.2rem; padding: 2px; margin: 1px; background-color: #eeeeee; width: 75%; height: 85%; display: flex; justify-content: center; align-items: center; border-radius: 4px; } .last-digits { color: #EF4B4B; } .digit-slide-up { display: flex; justify-content: center; align-items: center; height: 100%; width: 100%; animation: slide-up 0.5s cubic-bezier(0.16,1,0.3,1) forwards; } @keyframes slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
模型类仅包含整数列表和转换为字符串的方法。
问题分析与修复方案
你的核心问题在于:每次数字更新时,Blazor会直接替换整个digit-slide-up元素,动画仅在元素首次渲染时执行一次,后续替换元素无法触发动画;同时你没有保留旧数字的过渡逻辑,自然看不到动效。
修复步骤:
- 每个数字容器同时包含旧数字和新数字,通过容器滚动实现切换效果
- 数字更新时动态触发动画类,动画结束后重置状态
- 调整CSS布局,让数字列表可垂直滚动
修改后的代码示例:
CurrentMeterDisplayContainer.razor
@rendermode InteractiveAuto @implements IDisposable @using System.Threading <div class="meter-container"> <div class="meter-reading"> @for (int i = 0; i < meterReading.CurrentValue.Length; i++) { var index = i; var currentDigit = meterReading.CurrentValue[index]; var previousDigit = meterReading.PreviousValue.Length > index ? meterReading.PreviousValue[index] : '0'; <div class="digit-container"> <div class="@($"digit-slide-container {(!string.IsNullOrEmpty(meterReading.PreviousValue) ? "animate-slide-up" : "")}")"> <span class="@(index >= meterReading.CurrentValue.Length - 2 ? "digit last-digits" : "digit")">@previousDigit</span> <span class="@(index >= meterReading.CurrentValue.Length - 2 ? "digit last-digits" : "digit")">@currentDigit</span> </div> </div> } </div> </div> @code { private CurrentMeterReadings meterReading = new CurrentMeterReadings(); private Timer timer; private bool isAnimating; protected override void OnInitialized() { meterReading.UpdateReading(); timer = new Timer(_ => UpdateMeter(), null, 0, 5000); } private async void UpdateMeter() { if (isAnimating) return; isAnimating = true; meterReading.UpdateReading(); await InvokeAsync(StateHasChanged); // 等待动画完成后重置状态 await Task.Delay(500); isAnimating = false; await InvokeAsync(StateHasChanged); } public void Dispose() { timer?.Dispose(); } }
模型类修改(示例)
public class CurrentMeterReadings { public string CurrentValue { get; private set; } public string PreviousValue { get; private set; } = string.Empty; public void UpdateReading() { PreviousValue = CurrentValue; // 模拟生成新的6位仪表读数 CurrentValue = new Random().Next(100000, 999999).ToString(); } }
CurrentMeterDisplayContainer.razor.css 修改
.meter-container { padding: 2px; background-color: #fff; border-radius: 5px; } .meter-reading { display: flex; } .digit-container { margin: 2px; background-color: #fff; border-radius: 5px; width: 22px; height: 40px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); overflow: hidden; position: relative; } .digit-slide-container { position: absolute; top: 0; left: 0; width: 100%; height: 200%; /* 容纳两个数字,高度为容器的2倍 */ display: flex; flex-direction: column; transition: transform 0.5s cubic-bezier(0.16,1,0.3,1); } .animate-slide-up { transform: translateY(-50%); /* 向上滚动50%,显示新数字 */ } .digit { font-family: "Bebas Regular"; font-size: 1.2rem; padding: 2px; background-color: #eeeeee; width: 75%; height: 40px; /* 和容器高度一致 */ display: flex; justify-content: center; align-items: center; border-radius: 4px; margin: 0 auto; } .last-digits { color: #EF4B4B; }
核心原理:
- 每个数字容器内垂直排列旧值和新值,容器固定高度并隐藏溢出内容
- 数字更新时给容器添加动画类,通过
transform让数字列表向上滚动,显示新数字 - 动画完成后重置状态,确保下次更新能重新触发动画
内容的提问来源于stack exchange,提问作者Amirul.gg
相关产品推荐
相关产品推荐

