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

.NET MAUI中如何实现支持自定义区间动画的不确定进度条

.NET MAUI 指定区间往返式不确定进度条实现方案

原生ProgressBar控件确实没有内置区间循环动画能力,可以通过以下两种方案实现需求,不需要引入第三方控件。


方案1:基于原生动画API实现轻量化区间循环(推荐)

直接使用MAUI内置的Animation类和ProgressTo方法组合,实现自定义区间的往返循环动画,支持自定义进度范围、动画时长、缓动效果,代码侵入性最低。

首先为ProgressBar写通用扩展方法,封装动画的启动和停止逻辑,支持取消令牌避免内存泄漏:

public static class ProgressBarExtensions
{
    /// <summary>
    /// 启动不确定进度的区间往返循环动画
    /// </summary>
    /// <param name="minProgress">区间最小进度值,范围0-1</param>
    /// <param name="maxProgress">区间最大进度值,范围0-1</param>
    /// <param name="singleDirectionDuration">单方向(最小到最大/最大到最小)动画时长,单位毫秒</param>
    /// <param name="cancellationToken">动画取消令牌,页面销毁/操作结束时触发取消</param>
    public static void StartIndeterminateLoop(
        this ProgressBar progressBar,
        double minProgress = 0.2,
        double maxProgress = 0.8,
        uint singleDirectionDuration = 800,
        CancellationToken cancellationToken = default)
    {
        progressBar.Progress = minProgress;

        var loopAnimation = new Animation(async _ =>
        {
            if (cancellationToken.IsCancellationRequested) return;
            // 从最小值平滑过渡到最大值
            await progressBar.ProgressTo(maxProgress, singleDirectionDuration, Easing.CubicInOut);
            
            if (cancellationToken.IsCancellationRequested) return;
            // 从最大值平滑过渡回最小值
            await progressBar.ProgressTo(minProgress, singleDirectionDuration, Easing.CubicInOut);
        }, minProgress, maxProgress);

        // 提交动画,设置为持续循环直到收到取消信号
        loopAnimation.Commit(
            owner: progressBar,
            name: "IndeterminateProgressLoop",
            rate: 16,
            repeat: () => !cancellationToken.IsCancellationRequested);
    }

    /// <summary>
    /// 停止不确定进度动画
    /// </summary>
    /// <param name="finalProgress">动画停止后最终展示的进度值,默认1(100%)</param>
    public static void StopIndeterminateLoop(this ProgressBar progressBar, double finalProgress = 1d)
    {
        progressBar.AbortAnimation("IndeterminateProgressLoop");
        progressBar.ProgressTo(finalProgress, 250, Easing.CubicOut);
    }
}

调用方式非常简单,在长耗时操作启动时开启动画,操作结束后停止即可:

// 初始化取消令牌源
var animationCts = new CancellationTokenSource();

try
{
    // 启动25%-50%区间的往返动画,单趟动画时长600毫秒
    progressBar.StartIndeterminateLoop(
        minProgress: 0.25,
        maxProgress: 0.5,
        singleDirectionDuration: 600,
        cancellationToken: animationCts.Token);

    // 执行你的长耗时业务逻辑
    await RunLongRunningBusinessAsync();
}
finally
{
    // 无论操作成功/失败/取消,都停止动画释放资源
    animationCts.Cancel();
    progressBar.StopIndeterminateLoop();
    animationCts.Dispose();
}

方案2:自定义绘制实现完全对齐设计规范的效果

如果需要完全匹配Material设计规范中不确定进度条的动效(包含活动块宽度动态变化、滑动节奏匹配),可以通过GraphicsView自绘实现,自由度更高:

  • 自定义可绑定的动画进度属性,值范围0-1,用循环动画持续驱动该值变化
  • 重写绘制逻辑,根据当前动画进度值动态计算活动进度块的位置、宽度,绘制轨道和活动块
  • 动画驱动逻辑和方案1一致,和UI刷新帧率同步,不会出现卡顿

注意事项

  • 不要用while(true)+Task.Delay的方式手动修改Progress值实现动画,这种方式无法和UI刷新频率同步,容易出现掉帧、卡顿问题
  • 必须在页面消失、长操作结束时主动中止动画,避免动画持续运行占用UI线程资源、引发内存泄漏
  • 默认使用CubicInOut缓动函数更符合通用设计动效节奏,如果需要匀速动画,将代码中的Easing替换为Easing.Linear即可

内容的提问来源于stack exchange,提问作者John Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:15:42