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

如何为WPF TextBlock的DoubleAnimation设置时长以保持恒定滚动速度

实现WPF跑马灯连续无缝滚动方案

核心思路

不用等待单条文本完全移出可视区域再切换内容,而是让新文本在旧文本未完全离开时就衔接跟上。通过复用TextBlock容器、维护文本队列,结合你已实现的恒定速度计算逻辑,实现连续滚动流效果。

具体实现步骤

1. 调整布局:用横向StackPanel承载滚动内容

把原有单个TextBlock替换为横向排列的StackPanel,内部放两个TextBlock用于交替显示新旧文本,还可在文本间添加间隔提升可读性。

XAML示例:

<Canvas x:Name="canMain" ClipToBounds="True">
    <StackPanel x:Name="spMarquee" Orientation="Horizontal">
        <TextBlock x:Name="tbMarquee1" Margin="0 0 20 0"/> <!-- 文本间保留20px间隔 -->
        <TextBlock x:Name="tbMarquee2" Margin="0 0 20 0"/>
    </StackPanel>
</Canvas>

2. 维护滚动状态与文本队列

定义变量跟踪当前滚动的TextBlock、待显示文本队列,复用你已有的速度系数_speedFactor控制滚动快慢。

private Queue<string> _textQueue = new Queue<string>();
private TextBlock _currentTb;
private TextBlock _nextTb;
private double _speedFactor = 0.02; // 你已实现的速度系数
private bool _isScrolling = false;

3. 复用恒定速度动画逻辑

你之前用(文本宽度 + 容器宽度) * _speedFactor计算动画时长的逻辑是正确的——速度=距离/时间,通过这个公式计算时长,能保证无论文本长短,滚动速度始终恒定。

4. 无缝衔接的滚动触发逻辑

当需要更新文本时,先将新文本加入队列。若当前未在滚动,直接启动第一条文本的滚动;若正在滚动,等当前文本滚动到自身宽度完全进入容器时,启动下一个TextBlock的滚动,让它刚好衔接在当前文本尾部。

核心滚动方法:

private async void StartMarqueeScroll()
{
    while (_textQueue.Count > 0 || _isScrolling)
    {
        if (!_isScrolling && _textQueue.Count > 0)
        {
            // 交替切换当前和备用TextBlock
            (_currentTb, _nextTb) = (_nextTb == null ? tbMarquee1 : _nextTb, _currentTb == null ? tbMarquee2 : _currentTb);
            _currentTb.Text = _textQueue.Dequeue();
            _currentTb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            double textWidth = _currentTb.DesiredSize.Width;
            double totalScrollDistance = textWidth + canMain.ActualWidth;
            double animationDuration = totalScrollDistance * _speedFactor;

            // 重置当前TextBlock位置:放在容器右侧外部
            Canvas.SetLeft(_currentTb, canMain.ActualWidth);
            _isScrolling = true;

            // 启动当前文本滚动动画
            DoubleAnimation scrollAnim = new DoubleAnimation
            {
                From = canMain.ActualWidth,
                To = -textWidth,
                Duration = TimeSpan.FromSeconds(animationDuration),
                FillBehavior = FillBehavior.Stop
            };

            scrollAnim.Completed += (s, e) =>
            {
                _isScrolling = false;
            };

            _currentTb.BeginAnimation(Canvas.LeftProperty, scrollAnim);

            // 提前触发下一个文本滚动:当前文本滚动完自身宽度时,启动下一个
            await Task.Delay(TimeSpan.FromSeconds(textWidth * _speedFactor));
            if (_textQueue.Count > 0)
            {
                _nextTb.Text = _textQueue.Dequeue();
                _nextTb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                double nextTextWidth = _nextTb.DesiredSize.Width;
                double nextTotalDistance = nextTextWidth + canMain.ActualWidth;
                double nextDuration = nextTotalDistance * _speedFactor;

                // 设置下一个文本初始位置:接在当前文本尾部+间隔
                Canvas.SetLeft(_nextTb, canMain.ActualWidth + textWidth + 20);
                DoubleAnimation nextAnim = new DoubleAnimation
                {
                    From = canMain.ActualWidth + textWidth + 20,
                    To = -nextTextWidth,
                    Duration = TimeSpan.FromSeconds(nextDuration),
                    FillBehavior = FillBehavior.Stop
                };

                nextAnim.Completed += (s, e) =>
                {
                    if (_textQueue.Count > 0)
                    {
                        StartMarqueeScroll();
                    }
                };

                _nextTb.BeginAnimation(Canvas.LeftProperty, nextAnim);
            }
        }
        await Task.Delay(100);
    }
}

5. 文本更新调用

当需要添加新滚动文本时,直接加入队列,若未在滚动则启动滚动流程:

public void AddMarqueeText(string text)
{
    _textQueue.Enqueue(text);
    if (!_isScrolling)
    {
        StartMarqueeScroll();
    }
}

关键细节说明

  • 文本间隔:通过Margin给文本间留空白,避免内容拥挤,提升可读性。
  • 控件复用:用两个TextBlock交替使用,避免频繁创建销毁控件,优化性能。
  • 恒定速度保障:始终用(文本宽度 + 容器宽度) * _speedFactor计算动画时长,确保滚动速度不受文本长度影响。
  • 无缝衔接时机:当前文本滚动完自身宽度后启动下一个文本,此时新文本刚好出现在容器右侧,与旧文本尾部衔接,形成连续流。

内容的提问来源于stack exchange,提问作者D.Man

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:53:10