【问题标题】:Animate (smoothly) ScrollViewer programmatically以编程方式动画(平滑)ScrollViewer
【发布时间】:2014-10-04 23:03:35
【问题描述】:

有没有办法在 Windows Phone 8.1 Runtime 中平滑地为ScrollViewers 垂直偏移设置动画?

我尝试使用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);
        }
    }
}

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

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();

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

【问题讨论】:

  • 您可以尝试的一件事是WinRTXamlToolkit's ScrollToVerticalOffsetWithAnimation extension。您可以手动实现它或通过 Nuget 添加库。
  • 我不认为,有办法让它完全平滑。这种动画可能不是硬件加速的。
  • @MikkoVitala 首先,使用这种方法会失去虚拟化,因为您将 ListBox 放在 ScrollViewer 中。这个示例是针对 WP8.0 的,我也实现了相同的行为,但是在 WP8 中这很顺利,因为没有依赖和独立动画的概念,所有动画都是独立的并且在合成线程上运行。在 WP8.1 中,依赖动画在 UI 线程上运行,因此您会失去流畅的动画。但无论如何,谢谢你的链接。
  • 还在寻找这个问题的答案吗?

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


【解决方案1】:

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

如果没有看到 ChangeView 不起作用的代码,很难猜测到底发生了什么,但您可以尝试一些事情。

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

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

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

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

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

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

我已经在answerUpdate 2 部分中封装了上述逻辑(感谢这个问题,我意识到当虚拟化开启时我的初始答案不起作用:p)。告诉我你的进展情况。

【讨论】:

  • 第一种方法使ChangeView 平滑,因为Task.Delay。我不知道这会有这么大的帮助。非常感谢。
【解决方案2】:

我认为这里已经回答了这个问题:

Animated (Smooth) scrolling on ScrollViewer

还有 WinRT XAML Toolki,它提供了“一种通过动画将 ScrollViewer 滚动到指定偏移量的方法”:

http://winrtxamltoolkit.codeplex.com/

【讨论】:

  • 查看ScrollViewerExtensions.cs的源码,创建动画的方法名为ScrollToVerticalOffsetWithAnimation,可以看到da.EnableDependentAnimation = true这行表示动画没有运行在合成线程上,这意味着它可能运行不流畅,这是主要问题,如何使用 独立动画 为滚动查看器设置动画
  • 这些都与 WinRT/Windows10 无关
【解决方案3】:

由于 ScrollToVerticalOffset 在 Windows 10 的较新版本中已弃用/过时(使 ScrollViewOffSetMediator 扩展控件不再工作),并且新的 ChangeView 方法实际上并未提供流畅或可控的动画,因此需要新的解决方案。请在此处查看我的答案,它允许您平滑为 ScrollViewer 及其内容设置动画并将其缩放到任何所需位置,而不管应用程序的最终用户最初将滚动条放置在何处:

How to scroll to element in UWP

【讨论】:

    【解决方案4】:

    我相信this article 是您正在寻找的东西,而且他使用的方法似乎对您有用。

    快捷方式:

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

    2. 复制您的滚动查看器

    3. 使用动画师。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 2015-07-18
      • 1970-01-01
      • 2014-09-14
      • 2016-07-06
      • 2016-08-01
      • 1970-01-01
      相关资源
      最近更新 更多