3

为 a 中的滚动设置动画ScrollViewer似乎是一项常见任务。我使用计时器实现了它,类似于在此处找到的方法。这种方法效果很好,非常流畅,看起来很完美。

然而,现在我中包含的对象的复杂性和数量ScrollViewer增加了,动画看起来非常生涩。我觉得这很奇怪,因为如果我手动滚动它就可以正常工作。

    public void ShiftLeft(int speed = 11)
    {
        CustomTimer timer = new CustomTimer(); //DispatchTimer with "life"
        timer.Interval = new TimeSpan(0, 0, 0, 0, 5);
        timer.Tick += ((sender, e) =>
        {
            scrollViewer1.ScrollToHorizontalOffset(
                scrollViewer1.HorizontalOffset - (scrollViewer1.ScrollableWidth / (gridColumnCount - 3) / speed));
            if (scrollViewer1.HorizontalOffset == 0) //cant scroll any more
                ((CustomTimer)sender).Stop();
            ((CustomTimer)sender).life++;
            if (((CustomTimer)sender).life >= speed) //reached destination
                ((CustomTimer)sender).Stop();
        });
        timer.Start();
    }

我的方法是否有问题导致这种奇怪的抽搐?知道如何解决吗?

4

1 回答 1

4

CompositionTarget.Rendering将更适合动画事物,因为它会在每次即将渲染帧时触发。尝试这样的事情:

    public void Shift(ScrollViewer target, double speed = 11, double distance = 20)
    {
        double startOffset = target.HorizontalOffset;
        double destinationOffset = target.HorizontalOffset + distance;

        if (destinationOffset < 0)
        {
            destinationOffset = 0;
            distance = target.HorizontalOffset;
        }

        if (destinationOffset > target.ScrollableWidth)
        {
            destinationOffset = target.ScrollableWidth;
            distance = target.ScrollableWidth - target.HorizontalOffset;
        }

        double animationTime = distance / speed;
        DateTime startTime = DateTime.Now;

        EventHandler renderHandler = null;

        renderHandler = (sender, args) =>
        {
            double elapsed = (DateTime.Now - startTime).TotalSeconds;

            if (elapsed >= animationTime)
            {
                target.ScrollToHorizontalOffset(destinationOffset);
                CompositionTarget.Rendering -= renderHandler;
            }

            target.ScrollToHorizontalOffset(startOffset + (elapsed * speed));
        };

        CompositionTarget.Rendering += renderHandler;
    }

编辑:添加范围检查

使用负距离值向左滚动。

编辑2:

您可能希望使用此CompositionTargetEx实现而不是CompositionTarget,因为它只会在渲染线程实际绘制新帧时触发:

https://stackoverflow.com/a/16334423/612510

编辑 3:

由于您使用的是 WPF(而不是 Silverlight,就​​像我更习惯的那​​样),您可以使用该类而不是我的方法Stopwatch来测量经过的秒数。DateTime.Now

于 2013-07-29T18:09:15.517 回答