以编程方式动画(平滑)ScrollViewer

时间:2014-10-04 23:03:35

标签: c# windows-runtime winrt-xaml windows-phone-8.1

有没有办法在Windows Phone 8.1 Runtime中平滑动画ScrollViewer垂直偏移?

我尝试过使用ScrollViewer.ChangeView()方法,无论是否将disableAnimation参数设置为true或false,都不会对垂直偏移的更改进行动画处理。

例如:myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false); 没有动画就会更改偏移量。

我也尝试过使用垂直偏移介体:

/// <summary>
/// Mediator that forwards Offset property changes on to a ScrollViewer
/// instance to enable the animation of Horizontal/VerticalOffset.
/// </summary>
public sealed class ScrollViewerOffsetMediator : FrameworkElement
{
    /// <summary>
    /// ScrollViewer instance to forward Offset changes on to.
    /// </summary>
    public ScrollViewer ScrollViewer
    {
        get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
        set { SetValue(ScrollViewerProperty, value); }
    }
    public static readonly DependencyProperty ScrollViewerProperty =
            DependencyProperty.Register("ScrollViewer",
            typeof(ScrollViewer),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(null, OnScrollViewerChanged));
    private static void OnScrollViewerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        var scrollViewer = (ScrollViewer)(e.NewValue);
        if (null != scrollViewer)
        {
            scrollViewer.ScrollToVerticalOffset(mediator.VerticalOffset);
        }
    }

    /// <summary>
    /// VerticalOffset property to forward to the ScrollViewer.
    /// </summary>
    public double VerticalOffset
    {
        get { return (double)GetValue(VerticalOffsetProperty); }
        set { SetValue(VerticalOffsetProperty, value); }
    }
    public static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.Register("VerticalOffset",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(0.0, OnVerticalOffsetChanged));
    public static void OnVerticalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        if (null != mediator.ScrollViewer)
        {
            mediator.ScrollViewer.ScrollToVerticalOffset((double)(e.NewValue));
        }
    }

    /// <summary>
    /// Multiplier for ScrollableHeight property to forward to the ScrollViewer.
    /// </summary>
    /// <remarks>
    /// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
    /// </remarks>
    public double ScrollableHeightMultiplier
    {
        get { return (double)GetValue(ScrollableHeightMultiplierProperty); }
        set { SetValue(ScrollableHeightMultiplierProperty, value); }
    }
    public static readonly DependencyProperty ScrollableHeightMultiplierProperty =
            DependencyProperty.Register("ScrollableHeightMultiplier",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(0.0, OnScrollableHeightMultiplierChanged));
    public static void OnScrollableHeightMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        var scrollViewer = mediator.ScrollViewer;
        if (null != scrollViewer)
        {
            scrollViewer.ScrollToVerticalOffset((double)(e.NewValue) * scrollViewer.ScrollableHeight);
        }
    }
}

我可以使用VerticalOffsetDoubleAnimation属性设置动画:

Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.EnableDependentAnimation = true;
da.From = Mediator.ScrollViewer.VerticalOffset;
da.To = da.From + p;
da.Duration = new Duration(TimeSpan.FromMilliseconds(300));
da.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut };
Storyboard.SetTarget(da, Mediator);
Storyboard.SetTargetProperty(da, "(Mediator.VerticalOffset)");
sb.Children.Add(da);

sb.Begin();

Mediator在XAML中声明。 但是这个动画在我的设备上并不流畅(Lumia 930)。

4 个答案:

答案 0 :(得分:15)

无论数据虚拟化是否开启,您都应该坚持使用ChangeView来滚动动画。

如果没有看到ChangeView不起作用的代码,就很难猜出实际发生了什么,但有几件事你可以试试。

第一种方法是在调用Task.Delay(1)之前添加ChangeView,只是为了给操作系统一些时间来完成其他并发UI任务。

await Task.Delay(1);
scrollViewer.ChangeView(null, scrollViewer.ScrollableHeight, null, false);

第二种方法有点复杂。我注意到的是,当ListView中有许多复杂项目时,从第一项到最后一项(来自ChangeView方法)的滚动动画根本不是很平滑。

这是因为ListView首先需要实现/渲染许多项目,因为数据虚拟化然后进行动画滚动。效率不高的恕我直言。

我想出的是 - 首先,使用非动画ListView.ScrollIntoView滚动到最后一项只是为了实现它。然后,调用ChangeView将偏移量移动到ActualHeight * 2 ListView的大小并禁用动画(您可以根据应用的滚动体验将其更改为您想要的任何大小) 。最后,再次调用ChangeView以回滚到最后,这次是动画。这样做会提供更好的滚动体验,因为滚动距离只是ActualHeight的{​​{1}}。

请记住,当您想要滚动到的项目已在UI上实现时,您不希望在上面执行任何操作。您只需计算此项与ListView顶部之间的距离,然后调用ScrollViewer滚动到该项目。

我已将此逻辑包含在此answer Update 2 部分中(由于这个问题,我意识到我的初始答案在启用虚拟化时不起作用:p)。让我知道你是怎么走的。

答案 1 :(得分:4)

我认为这个问题已在这里得到解答:

Animated (Smooth) scrolling on ScrollViewer

还有WinRT XAML Toolki,它提供了一种将ScrollViewer滚动到带动画的指定偏移量的方法&#34;:

http://winrtxamltoolkit.codeplex.com/

答案 2 :(得分:0)

在新版Windows 10中不推荐使用ScrollToVerticalOffset(让ScrollViewOffSetMediator扩展控件不再工作),而新的ChangeView方法实际上并没有提供平滑或可控制的动画,因此需要一个新的解决方案。请在此处查看我的答案,无论应用程序的最终用户最初放置滚动条的位置如何,都可以顺利地动画并将ScrollViewer及其内容缩放到任何所需位置:

How to scroll to element in UWP

答案 3 :(得分:0)

我相信您正在寻找this article,看来他使用的方法正在为您服务。

快捷方式:

  1. 手动将偏移量依赖性参数添加到 scrollviewer

  2. 复制滚动查看器

  3. 使用动画师。