Conseils et astuces sur les animations

Lorsque vous utilisez des animations dans WPF, de nombreux conseils et astuces peuvent vous permettre d'optimiser vos animations en vous évitant toute frustration.

Problèmes généraux

La position de la barre de défilement ou du curseur se fige après avoir été animée

Si vous animez la position d'une barre de défilement ou d'un curseur en utilisant une animation qui a un FillBehavior dont la valeur estHoldEnd (valeur par défaut), l'utilisateur ne pourra plus déplacer la barre de défilement ni le curseur. Cela est dû au fait que, même si l'animation est terminée, elle remplace toujours la valeur de base de la propriété cible. Pour stopper le remplacement de la valeur actuelle de la propriété, supprimez l'animation ou donnez-lui un FillBehavior avec une valeur Stop. Pour obtenir des informations supplémentaires et un exemple, consultez Comment : définir une propriété après l'avoir animée avec un storyboard.

L'animation de la sortie d'une animation n'a aucun effet

Vous ne pouvez pas animer un objet qui correspond à la sortie d'une autre animation. Par exemple, si vous utilisez un ObjectAnimationUsingKeyFrames pour animer le Fill d'un Rectangle à partir d'un RadialGradientBrush vers un SolidColorBrush, vous ne pouvez animer aucune des propriétés du RadialGradientBrush ni du SolidColorBrush quelles qu'elles soient.

Impossible de modifier la valeur d'une propriété après l'avoir animée

Dans certains cas, il se peut que vous ne puissiez pas changer la valeur d'une propriété après l'avoir animée, même si l'animation est terminée. Cela est dû au fait que, même si l'animation est terminée, elle remplace toujours la valeur de base de la propriété. Pour stopper le remplacement de la valeur actuelle de la propriété, supprimez l'animation ou donnez-lui un FillBehavior avec une valeur Stop. Pour obtenir des informations supplémentaires et un exemple, consultez Comment : définir une propriété après l'avoir animée avec un storyboard.

La modification d'une chronologie n'a aucun effet

Même si la plupart des propriétés Timeline peuvent être animées et liées aux données, la modification des valeurs de propriété d'un Timeline actif semble n'avoir aucun effet. Cela est dû au fait que, lorsqu'un Timeline a commencé, le système de minuterie fait une copie du Timeline et l'utilise pour créer un objet Clock. La modification de l'original n'a aucun effet sur la copie du système.

Pour qu'un Timeline reflète des modifications, son horloge doit être régénérée et utilisée pour remplacer l'horloge créée précédemment. Les horloges ne sont pas régénérées automatiquement. Voici plusieurs façons d'appliquer des modifications de chronologie :

  • Si la chronologie est ou appartient à un Storyboard, vous pouvez alors faire en sorte qu'elle reflète les modifications, en réappliquant sa table de montage séquentiel à l'aide d'un BeginStoryboard ou de la méthode Begin. Cela redémarre par contre également l'animation. Dans le code, vous pouvez utiliser la méthode Seek pour replacer la table de montage séquentiel à sa position précédente.

  • Si vous avez appliqué directement une animation à une propriété à l'aide de la méthode BeginAnimation, appelez une nouvelle fois la méthode BeginAnimation et passez-lui l'animation qui a été modifiée.

  • Si vous travaillez directement au niveau de l'horloge, créez et appliquez un nouveau jeu d'horloges et utilisez-les pour remplacer le précédent jeu d'horloges générées.

Pour plus d'informations sur les horloges et les chronologies, consultez Vue d'ensemble de l'animation et du système de minutage.

FillBehavior.Stop ne fonctionne pas comme prévu

Il arrive parfois que, lorsque vous affectez à la propriété FillBehavior la valeur Stop, cela semble n'avoir aucun effet, par exemple quand une animation est « remise » à une autre car son HandoffBehavior a la valeur SnapshotAndReplace.

L'exemple suivant crée Canvas, Rectangle et TranslateTransform. Le TranslateTransform sera animé pour déplacer Rectangle autour de Canvas.

<Canvas Width="600" Height="200">
  <Rectangle 
    Canvas.Top="50" Canvas.Left="0" 
    Width="50" Height="50" Fill="Red">
    <Rectangle.RenderTransform>
      <TranslateTransform 
        x:Name="MyTranslateTransform" 
        X="0" Y="0" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

L'exemple dans cette section utilise les objets précédents pour décrire plusieurs cas où la propriété FillBehavior ne se comporte pas de la façon attendue.

FillBehavior="Stop" et HandoffBehavior avec plusieurs animations

Vous avez parfois l'impression qu'une animation ignore sa propriété FillBehavior lorsqu'elle est remplacée par une seconde animation. Prenons l'exemple suivant, dans lequel deux objets Storyboard sont créés et utilisés pour animer le même TranslateTransform indiqué dans l'exemple précédent.

Le premier Storyboard, B1, anime la propriété X de TranslateTransform de 0 à 350, ce qui déplace le rectangle de 350 pixels vers la droite. Lorsque l'animation arrive à la fin de sa durée et s'arrête, la propriété X revient à sa valeur d'origine, 0. Par conséquent, le rectangle se déplace de 350 pixels vers la droite, puis revient à sa position d'origine.

<Button Content="Start Storyboard B1">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard x:Name="B1">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            From="0" To="350" Duration="0:0:5"
            FillBehavior="Stop"
            />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Le second Storyboard, B2, anime également la propriété X du même TranslateTransform. Puisque seule la propriété To de l'animation dans ce Storyboard est définie, l'animation utilise la valeur actuelle de la propriété qu'elle anime comme valeur de départ.


<!-- Animates the same object and property as the preceding
     Storyboard. -->
<Button Content="Start Storyboard B2">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard x:Name="B2">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            To="500" Duration="0:0:5" 
            FillBehavior="Stop" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Si vous cliquez sur le second bouton alors que le premier Storyboard est en cours de lecture, le comportement suivant est susceptible de se produire :

  1. La première table de montage séquentiel se termine et remet le rectangle à sa position d'origine, car l'animation a un FillBehavior dont la valeur est Stop.

  2. La seconde table de montage séquentiel est appliquée et s'anime à partir de la position actuelle, qui est maintenant de 0, jusqu'à 500.

Mais ce n'est pas ce qu'il se passe. Au lieu de revenir à sa position d'origine, le rectangle continue à se déplacer vers la droite. Cela est dû au fait que la seconde animation utilise la valeur actuelle de la première animation comme valeur de départ et s'anime à partir de cette valeur jusqu'à 500. Lorsque la seconde animation remplace la première puisque SnapshotAndReplace HandoffBehavior est utilisé, le FillBehavior de la première animation n'a pas d'importance.

FillBehavior et événement terminé

Dans le scénario décrit dans les exemples ci-après, Stop FillBehavior semble n'avoir aucun effet. Ici encore, l'exemple utilise un storyboard pour animer la propriété X de TranslateTransform de 0 à 350. Cependant, cet exemple s'inscrit cette fois à l'événement Completed.

<Button Content="Start Storyboard C">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard Completed="StoryboardC_Completed">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            From="0" To="350" Duration="0:0:5"
            FillBehavior="Stop" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Le gestionnaire d'événements Completed démarre un autre Storyboard qui anime la même propriété à partir de sa valeur actuelle jusqu'à 500.

        Private Sub StoryboardC_Completed(ByVal sender As Object, ByVal e As EventArgs)

            Dim translationAnimationStoryboard As Storyboard = CType(Me.Resources("TranslationAnimationStoryboardResource"), Storyboard)
            translationAnimationStoryboard.Begin(Me)
        End Sub
private void StoryboardC_Completed(object sender, EventArgs e)
{

    Storyboard translationAnimationStoryboard =
        (Storyboard)this.Resources["TranslationAnimationStoryboardResource"];
    translationAnimationStoryboard.Begin(this);
}

Le balisage suivant définit le second Storyboard comme ressource.

<Page.Resources>
  <Storyboard x:Key="TranslationAnimationStoryboardResource">
    <DoubleAnimation 
      Storyboard.TargetName="MyTranslateTransform"
      Storyboard.TargetProperty="X"
      To="500" Duration="0:0:5" />
  </Storyboard>
</Page.Resources>

Lorsque vous exécutez le Storyboard, vous vous attendez à ce que la propriété X de TranslateTransform s'anime de 0 à 350, revienne à 0 une fois terminée (car son paramètre FillBehavior a la valeur Stop), puis s'anime de 0 à 500. Au lieu de cela, TranslateTransform s'anime de 0 à 350, puis à 500.

Cela est dû à l'ordre dans lequel WPF déclenche les événements et au fait que les valeurs de propriété sont mises en cache et ne sont pas recalculées sauf si la propriété est invalidée. L'événement Completed est traité en premier car il a été déclenché par la chronologie racine (le premier Storyboard). À ce stade, la propriété X renvoie toujours sa valeur animée car elle n'a pas encore été invalidée. Le second Storyboard utilise la valeur mise en cache comme valeur de départ et commence l'animation.

Performances

Les animations continuent à s'exécuter lorsque vous avez quitté une page

Lorsque vous naviguez loin d'une Page qui contient des animations en cours d'exécution, ces dernières continuent à s'exécuter jusqu'à ce que Page soit récupéré par le garbage collector. Selon le système de navigation utilisé, lorsque vous quittez une page, elle peut rester en mémoire pendant une durée indéfinie, au cours de laquelle ses animations consomment des ressources. Cela est particulièrement visible lorsqu'une page contient des animations qui s'exécutent constamment (« ambiantes »).

C'est pourquoi il est conseillé d'utiliser l'événement Unloaded pour supprimer les animations lorsque vous naviguez loin d'une page.

Il y a différentes manières de supprimer une animation. Vous pouvez utiliser les techniques suivantes pour supprimer les animations qui appartiennent à un Storyboard.

La technique suivante peut être utilisée quelle que soit la façon dont l'animation a été démarrée.

  • Pour supprimer des animations d'une propriété spécifique, utilisez la méthode BeginAnimation(DependencyProperty, AnimationTimeline). Spécifiez la propriété animée comme premier paramètre et null comme second paramètre. Cela supprime toutes les horloges d'animation de la propriété.

Pour plus d'informations sur les différentes manières d'animer les propriétés, consultez Vue d'ensemble des techniques d'animation de propriétés.

L'utilisation de Compose HandoffBehavior consomme des ressources système

Lorsque vous appliquez un Storyboard, un AnimationTimeline ou un AnimationClock à une propriété à l'aide de Compose HandoffBehavior, tous les objets Clock précédemment associés à cette propriété continuent d'utiliser les ressources système ; le système de minuterie ne supprimera pas automatiquement ces horloges.

Pour éviter tout problème de performances lors de l'application d'un grand nombre d'horloges à l'aide de Compose, vous devez supprimer la composition des horloges de la propriété animée une fois leur tâche terminée. Il existe plusieurs manières de supprimer une horloge.

Cela constitue principalement un problème pour les animations sur des objets qui ont une durée de vie importante. Lorsqu'un objet est récupéré par le garbage collector, ses horloges sont également déconnectées et récupérées.

Pour plus d'informations sur les objets d'horloge, consultez Vue d'ensemble de l'animation et du système de minutage.

Voir aussi

Concepts

Vue d'ensemble de l'animation