Подтвердить что ты не робот

Анимация (плавно) ScrollViewer программно

Есть ли способ плавно анимировать вертикальное смещение ScrollViewer в 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).

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 3

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

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