it-swarm.com.ru

Анимировать (плавно) ScrollViewer программно

Есть ли способ плавно анимировать вертикальное смещение ScrollViewers в Windows Phone 8.1 Runtime?

Я попытался использовать метод 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);
        }
    }
}

и я могу анимировать свойство VerticalOffset с помощью 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).

36
Kristian Vukusic

Вы должны придерживаться ChangeView для прокрутки анимаций независимо от того, включена виртуализация данных или нет. 

Не видя вашего кода, где ChangeView не работает, довольно сложно догадаться, что на самом деле происходит, но есть несколько вещей, которые вы можете попробовать.

Первый подход заключается в добавлении Task.Delay(1) перед вызовом ChangeView, просто чтобы дать ОС некоторое время для завершения других параллельных задач пользовательского интерфейса.

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

Второй подход немного сложнее. Я заметил, что когда в ListView имеется много сложных элементов, анимация прокрутки от первого до последнего (из метода ChangeView) совсем не очень плавная. 

Это связано с тем, что ListView сначала требует реализации/визуализации многих элементов в процессе виртуализации данных, а затем выполняет анимированную прокрутку. Не очень эффективно ИМХО.

Я придумал следующее: во-первых, используйте неанимированный ListView.ScrollIntoView, чтобы перейти к последнему элементу, просто чтобы реализовать его. Затем вызовите ChangeView, чтобы переместить смещение до размера ActualHeight * 2ListView с отключенной анимацией (вы можете изменить его на любой нужный вам размер, основываясь на опыте прокрутки в вашем приложении). Наконец, снова вызовите ChangeView, чтобы прокрутить назад до конца, на этот раз с анимацией. Выполнение этого даст гораздо лучший опыт прокрутки, потому что расстояние прокрутки - только ActualHeight из ListView.

Имейте в виду, что когда элемент, на который вы хотите перейти, уже реализован в пользовательском интерфейсе, вы не хотите ничего делать выше. Вы просто вычисляете расстояние между этим элементом и вершиной ScrollViewer и вызываете ChangeView, чтобы перейти к нему.

Я уже включил приведенную выше логику в этот раздел answer 's Update 2 (благодаря этому вопросу я понял, что мой первоначальный ответ не работает, когда включена виртуализация: p). Дайте мне знать, как вы идете.

15
Justin XL

Я думаю, что на этот вопрос уже ответили здесь:

Анимированная (плавная) прокрутка в ScrollViewer

Существует также WinRT XAML Toolki, который предоставляет «способ прокрутки ScrollViewer до указанного смещения с анимацией»:

http://winrtxamltoolkit.codeplex.com/

4
SalientGreen

Я считаю, что эта статья - это то, что вы ищете, и, похоже, метод, который он использовал, работает для вас.

Быстрый путь:

  1. Добавьте параметр зависимости смещения вручную в scrollviewer.

  2. Дублируйте своего прокрутки 

  3. Используйте аниматора.

0
user10141586

Поскольку ScrollToVerticalOffset устарел/устарел в новых сборках Windows 10 (при этом элемент управления расширением ScrollViewOffSetMediator больше не работает), а новый метод ChangeView фактически не обеспечивает плавную или контролируемую анимацию, необходимо новое решение. Пожалуйста, смотрите мой ответ здесь, который позволяет плавно анимировать и масштабировать ScrollViewer и его содержимое в любую желаемую позицию, независимо от того, где конечный пользователь приложения имеет первоначально расположенные полосы прокрутки:

Как прокрутить до элемента в UWP

0
zax