以编程方式(平滑地)对ScrollViewer进行动画处理 [英] Animate (smoothly) ScrollViewer programmatically
问题描述
有没有一种方法可以使Windows Phone 8.1 Runtime中的 ScrollViewer
垂直偏移平滑地动画?
Is there a way to smoothly animate a ScrollViewer
s 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屋!