wake-up-neo.com

Animieren Sie ScrollViewer programmgesteuert (reibungslos)

Gibt es eine Möglichkeit, einen vertikalen Versatz in ScrollViewers in Windows Phone 8.1 Runtime nahtlos zu animieren?

Ich habe versucht, die ScrollViewer.ChangeView()-Methode zu verwenden, und die Änderung des vertikalen Versatzes wird nicht animiert, auch wenn ich den disableAnimation-Parameter auf true oder false setze.

Zum Beispiel: myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false); Der Versatz wird ohne Animation geändert.

Ich habe auch versucht, einen vertikalen Offset-Mediator zu verwenden:

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

und ich kann die VerticalOffset-Eigenschaft mit DoubleAnimation animieren:

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

Mediator ist in XAML deklariert. Diese Animation ist jedoch auf meinem Gerät (Lumia 930) nicht glatt.

36

Sie sollten bei ChangeViewfür Scrollanimationen bleiben, unabhängig davon, ob die Datenvirtualisierung aktiviert ist oder nicht. 

Ohne Ihren Code zu sehen, bei dem ChangeViewnicht funktioniert, ist es schwer zu erraten, was wirklich vor sich geht, aber es gibt ein paar Dinge, die Sie ausprobieren können.

Der erste Ansatz besteht darin, eine Task.Delay(1) vor dem Aufruf von ChangeViewhinzuzufügen, um dem Betriebssystem etwas Zeit zu geben, um andere gleichzeitige UI-Tasks abzuschließen.

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

Der zweite Ansatz ist etwas komplexer. Was mir aufgefallen ist, ist, dass wenn Sie viele komplexe Elemente in der ListViewhaben, die Animation vom ersten bis zum letzten Element (von der ChangeViewname__-Methode) überhaupt nicht reibungslos verläuft. 

Dies liegt daran, dass ListViewzuerst auf Grund von Datenvirtualisierung viele Elemente auf dem Weg erkennen/rendern muss und dann den animierten Bildlauf durchführt. IMHO nicht sehr effizient.

Was mir dabei einfiel, war folgendes: Verwenden Sie zunächst einen nicht animierten ListView.ScrollIntoView, um zum letzten Element zu blättern, um es zu realisieren. Rufen Sie dann ChangeViewauf, um den Offset auf eine Größe des ActualHeight * 2 des ListViewzu verschieben, wobei die Animation deaktiviert ist (Sie können die Größe je nach Bildlauf der App in der gewünschten Größe ändern). Rufen Sie schließlich noch einmal ChangeViewauf, um zum Ende zu scrollen, diesmal mit Animation. Dadurch erhalten Sie ein viel besseres Bildlauferlebnis, da der Bildlaufabstand nur der ActualHeightdes ListViewist.

Denken Sie daran, dass, wenn das Element, zu dem Sie scrollen möchten, bereits auf der Benutzeroberfläche realisiert wurde, Sie nichts weiter oben tun möchten. Sie berechnen einfach die Entfernung zwischen diesem Objekt und dem oberen Rand von ScrollViewerund rufen ChangeViewauf, um zu ihm zu scrollen.

Ich habe die obige Logik bereits in diesem Abschnitt Antwort 's Update 2 eingeschlossen (dank dieser Frage wurde mir klar, dass meine erste Antwort nicht funktioniert, wenn die Virtualisierung aktiviert ist: p). Lass mich wissen, wie es dir geht.

15
Justin XL

Ich denke, diese Frage wurde hier bereits beantwortet:

Animiertes (glattes) Scrollen in ScrollViewer

Es gibt auch das WinRT XAML-Toolki, das "eine Möglichkeit bietet, einen ScrollViewer zum angegebenen Offset mit Animation zu scrollen":

http://winrtxamltoolkit.codeplex.com/

4
SalientGreen

Ich glaube, dieser Artikel ist das, wonach Sie suchen, und es scheint, dass die Methode, die er verwendet hat, für Sie funktioniert.

Schneller Weg:

  1. Fügen Sie den Parameter für die Offset-Abhängigkeit manuell zu scrollviewer hinzu.

  2. Duplizieren Sie Ihren Scrollviewer 

  3. Verwenden Sie einen Animator.

0
user10141586

Da ScrollToVerticalOffset in neueren Builds von Windows 10 veraltet/veraltet ist (das ScrollViewOffSetMediator-Erweiterungssteuerelement nicht mehr funktioniert) und die neue ChangeView-Methode keine reibungslose oder kontrollierbare Animation bietet, ist eine neue Lösung erforderlich. Sehen Sie sich meine Antwort hier an, die es Ihnen erlaubt, reibungslosanimieren und den ScrollViewer und seinen Inhalt an jede gewünschte Position zu zoomen, unabhängig davon, wo der Endbenutzer der Anwendung die Bildlaufleisten ursprünglich positioniert hat:

So scrollen Sie zu einem Element in UWP

0
zax