以编程方式(平滑地)对ScrollViewer进行动画处理 [英] Animate (smoothly) ScrollViewer programmatically

查看:52
本文介绍了以编程方式(平滑地)对ScrollViewer进行动画处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有一种方法可以使Windows Phone 8.1 Runtime中的 ScrollViewer 垂直偏移平滑地动画?

Is there a way to smoothly animate a ScrollViewers vertical offset in Windows Phone 8.1 Runtime?

我已经尝试过使用 ScrollViewer.ChangeView()方法,并且无论将 disableAnimation 参数设置为true还是false,垂直偏移的更改都不是动画.

I have tried using the ScrollViewer.ChangeView() method and the change of vertical offset is not animated no matter if I set the disableAnimation parameter to true or false.

例如: myScrollViewer.ChangeView(null,myScrollViewer.VerticalOffset + p,null,false); 偏移量不使用动画即可更改.

For example: myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false); The offset is changed without animation.

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

I also tried using a vertical offset mediator:

/// <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);
        }
    }
}

,然后我可以使用 DoubleAnimation 设置 VerticalOffset 属性的动画:

and I can animate the VerticalOffset property with DoubleAnimation:

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)不流畅.

Mediator is declared in XAML. But this animation is not smooth on my device (Lumia 930).

推荐答案

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

You should stick with ChangeView for scrolling animations regardless of whether data virtualization is on or not.

没有看到您的代码在 ChangeView 不起作用的地方,很难猜测到底发生了什么,但是您可以尝试一些方法.

Without seeing your code where the ChangeView doesn't work, it's a bit hard to guess what's really going on but there are a couple of things that you can try.

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

First approach is to add a Task.Delay(1) before calling ChangeView, just to give the OS some time to finish off other concurrent UI tasks.

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

第二种方法稍微复杂一些.我注意到的是,当 ListView 中有许多复杂的项目时,从第一个项目到最后一个项目(从 ChangeView 方法)的滚动动画是'一点都没有.

The second approach is a bit more complex. What I've noticed is that, when you have many complex items in the ListView, the scrolling animation from the first item to the last (from the ChangeView method) isn't very smooth at all.

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

This is because the ListView first needs to realize/render many items along the way due to data virtualization and then does the animated scrolling. Not very efficient IMHO.

我想到的是-首先,使用非动画化的 ListView.ScrollIntoView 滚动到最后一项,以实现它.然后,在禁用动画的情况下,调用 ChangeView 将偏移量移动到 ListView ActualHeight * 2 的大小(您可以将其更改为根据您应用的滚动体验而定).最后,再次调用 ChangeView 以滚动回到末尾,这次是动画.这样做会带来更好的滚动体验,因为滚动距离只是 ListView ActualHeight .

What I came up with is this - First, use a non-animated ListView.ScrollIntoView to scroll to the last item just to get it realized. Then, call ChangeView to move the offset up to a size of the ActualHeight * 2 of the ListView with animation disabled (you can change it to whatever size you want based on your app's scrolling experience). Finally, call ChangeView again to scroll back to the end, with animation this time. Doing this will give a much better scrolling experience 'cause the scrolling distance is just the ActualHeight of the ListView.

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

Keep in mind that when the item you want to scroll to is already realized on the UI, you don't want to do anything above. You simply just calculate the distance between this item and the top of the ScrollViewer and call ChangeView to scroll to it.

我已经在上述答案更新2 部分中包装了上面的逻辑(谢谢)对于这个问题,我意识到当在:p)上进行虚拟化时,我的最初答案不起作用.让我知道你的情况.

I already wrapped the logic above in this answer's Update 2 section (thanks to this question I realized my initial answer doesn't work when virtualization is on :p). Let me know how you go.

这篇关于以编程方式(平滑地)对ScrollViewer进行动画处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆