web-dev-qa-db-fra.com

Animer (en douceur) ScrollViewer par programmation

Existe-t-il un moyen d'animer en douceur un décalage vertical ScrollViewers dans Windows Phone 8.1 Runtime?

J'ai essayé d'utiliser la méthode ScrollViewer.ChangeView() et le changement de décalage vertical n'est pas animé, peu importe si je règle le paramètre disableAnimation sur true ou sur false.

Par exemple: myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false); Le décalage est modifié sans animation.

J'ai aussi essayé d'utiliser un médiateur de décalage vertical:

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

et je peux animer la propriété VerticalOffset avec 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();

Le médiateur est déclaré en XAML. Mais cette animation n’est pas fluide sur mon appareil (Lumia 930).

36
Kristian Vukusic

Vous devez vous en tenir à ChangeView pour le défilement des animations, que la virtualisation des données soit activée ou non. 

Sans voir votre code où la ChangeView ne fonctionne pas, il est un peu difficile de deviner ce qui se passe réellement, mais vous pouvez essayer plusieurs solutions.

La première approche consiste à ajouter un Task.Delay(1) avant d'appeler ChangeView, juste pour donner au système d'exploitation le temps de terminer d'autres tâches simultanées de l'interface utilisateur.

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

La deuxième approche est un peu plus complexe. Ce que j'ai remarqué, c'est que, lorsque vous avez plusieurs éléments complexes dans ListView, l'animation de défilement du premier élément au dernier (de la méthode ChangeView) n'est pas très fluide. 

Ceci est dû au fait que la ListView doit d’abord réaliser/restituer de nombreux éléments en cours de route en raison de la virtualisation des données, puis effectuer le défilement animé. Pas très efficace à mon humble avis.

Voici ce que je propose: Premièrement, utilisez un ListView.ScrollIntoView non animé pour faire défiler jusqu'au dernier élément, juste pour le réaliser. Ensuite, appelez ChangeView pour déplacer le décalage jusqu'à une taille du ActualHeight * 2 de la ListView avec animation désactivée (vous pouvez le changer à la taille souhaitée en fonction de l'expérience de défilement de votre application). Enfin, appelez à nouveau ChangeView pour revenir à la fin, avec animation cette fois. Faire ceci donnera une expérience de défilement bien meilleure car la distance de défilement est juste la ActualHeight de la ListView.

Gardez à l'esprit que lorsque l'élément que vous voulez faire défiler est déjà réalisé sur l'interface utilisateur, vous ne voulez rien faire ci-dessus. Vous calculez simplement la distance entre cet élément et le haut de la ScrollViewer et appelez ChangeView pour y accéder.

J'ai déjà enveloppé la logique ci-dessus dans cette section answer 's Update 2 (grâce à cette question, j'ai réalisé que ma réponse initiale ne fonctionnait pas lorsque la virtualisation est activée: p). Fais-moi savoir comment tu vas.

15
Justin XL

Je pense que cette question a déjà été répondu ici:

Défilement animé (lisse) sur ScrollViewer

Il existe également le WinRT XAML Toolki, qui fournit "un moyen de faire défiler un ScrollViewer vers un décalage spécifié avec animation":

http://winrtxamltoolkit.codeplex.com/

4
SalientGreen

Je crois cet article est ce que vous recherchez et il semble que la méthode qu'il a utilisée fonctionne pour vous.

Voie rapide:

  1. Ajoutez le paramètre de dépendance de décalage manuellement à scrollviewer .

  2. Dupliquez votre scrollviewer 

  3. Utilisez un animateur.

0
user10141586

ScrollToVerticalOffset obsolète/obsolète dans les versions plus récentes de Windows 10 (le contrôle de l'extension ScrollViewOffSetMediator ne fonctionnant plus) et la nouvelle méthode ChangeView ne fournissant pas une animation fluide ou contrôlable, une nouvelle solution est nécessaire. S'il vous plaît voir ma réponse ici qui permet à en douceur animer et zoomer le ScrollViewer et son contenu à n'importe quelle position désirée, indépendamment de l'endroit où l'utilisateur final de l'application a placé les barres de défilement initialement:

Comment faire défiler un élément dans UWP

0
zax