Conseils et astuces d’animation
Lorsque vous travaillez avec des animations dans WPF, il existe un certain nombre de conseils et astuces qui peuvent améliorer vos animations et vous sauver la frustration.
Problèmes généraux
L’animation de la position d’une barre de défilement ou d’un curseur le fige
Si vous animez la position d’une barre de défilement ou d’un curseur à l’aide d’une animation qui a un FillBehavior de HoldEnd (valeur par défaut), l’utilisateur ne pourra plus déplacer la barre de défilement ou le curseur. C’est parce que, même si l’animation s’est terminée, elle remplace toujours la valeur de base de la propriété cible. Pour empêcher l’animation de remplacer la valeur actuelle de la propriété, supprimez-la ou définissez une FillBehavior de Stop. Pour plus d’informations et un exemple, consultez Définir une propriété après l'avoir animée avec un storyboard.
Animer la sortie d'une animation n'a aucun effet
Vous ne pouvez pas animer un objet qui est la sortie d’une autre animation. Par exemple, si vous utilisez un ObjectAnimationUsingKeyFrames pour animer la Fill d’un Rectangle d’un RadialGradientBrush vers un SolidColorBrush, vous ne pouvez animer aucune propriété de l'RadialGradientBrush ou SolidColorBrush.
Impossible de modifier la valeur d’une propriété après l’avoir animée
Dans certains cas, il peut apparaître que vous ne pouvez pas modifier la valeur d’une propriété après qu’elle a été animée, même après la fin de l’animation. C’est parce que, même si l’animation s’est terminée, elle surcharge encore la valeur de base de la propriété. Pour empêcher l’animation de remplacer la valeur actuelle de la propriété, supprimez-la ou attribuez-lui une valeur de FillBehavior à Stop. Pour plus d’informations et un exemple, consultez Définir une propriété après l’animation avec un storyboard.
La modification d’une chronologie n’a aucun effet
Bien que la plupart des propriétés Timeline soient animatables et peuvent être liées aux données, la modification des valeurs de propriété d’un Timeline actif semble n’avoir aucun effet. C’est parce que, lorsqu’une Timeline est lancée, le système de minutage effectue 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’une Timeline reflète les 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 refléter les changements en réappliquant son storyboard à l'aide d'un BeginStoryboard ou de la méthode Begin. Cela a l’effet secondaire du redémarrage de l’animation. Dans le code, vous pouvez utiliser la méthode Seek pour revenir au storyboard à sa position précédente.
Si vous avez appliqué une animation directement à une propriété à l’aide de la méthode BeginAnimation, appelez à nouveau 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 nouvel ensemble d’horloges et utilisez-les pour remplacer l'ensemble précédent d’horloges générées.
Pour plus d’informations sur les chronologies et les horloges, consultez Vue d’ensemble de l’animation et du système de minutage.
FillBehavior.Stop ne fonctionne pas comme prévu
Il existe des moments où la définition de la propriété FillBehavior sur Stop semble n'avoir aucun effet, par exemple lorsqu'une animation « passe » à une autre parce qu'elle a un paramètre HandoffBehavior de SnapshotAndReplace.
L’exemple suivant crée un Canvas, un Rectangle et un TranslateTransform. Le TranslateTransform sera animé pour déplacer le Rectangle autour du 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>
Les exemples de cette section utilisent les objets précédents pour illustrer plusieurs cas où la propriété FillBehavior ne se comporte pas comme prévu.
FillBehavior="Stop » et HandoffBehavior avec plusieurs animations
Il semble parfois qu’une animation ignore sa propriété FillBehavior lorsqu’elle est remplacée par une deuxième animation. Prenez l’exemple suivant, qui crée deux objets Storyboard et les utilise pour animer la même TranslateTransform illustrée dans l’exemple précédent.
La première Storyboard, B1
, anime la propriété X du TranslateTransform de 0 à 350, ce qui déplace le rectangle de 350 pixels vers la droite. Lorsque l’animation atteint la fin de sa durée et cesse de jouer, la propriété X rétablit sa valeur d’origine, 0. Par conséquent, le rectangle se déplace à droite de 350 pixels, 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 deuxième Storyboard, à savoir B2
, anime également la propriété X du même TranslateTransform. Étant donné que seule la propriété To de l’animation dans cette 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 deuxième bouton pendant que la première Storyboard est en cours de lecture, vous pouvez vous attendre au comportement suivant :
Le premier storyboard se termine et renvoie le rectangle à sa position d’origine, car l’animation a une FillBehavior de Stop.
Le deuxième storyboard prend effet et s’anime de la position actuelle, qui est maintenant 0, à 500.
Mais ce n’est pas ce qui se passe. Au lieu de cela, le rectangle ne revient pas ; il continue de se déplacer vers la droite. Cela est dû au fait que la deuxième animation utilise la valeur actuelle de la première animation comme valeur de départ et s’anime de cette valeur à 500. Lorsque la deuxième animation remplace la première, car la SnapshotAndReplaceHandoffBehavior est utilisée, la FillBehavior de la première animation n’a pas d’importance.
Comportement de remplissage et événement de fin
Les exemples suivants illustrent un autre scénario dans lequel le StopFillBehavior semble n’avoir aucun effet. Encore une fois, l’exemple utilise un Storyboard pour animer la propriété X du TranslateTransform de 0 à 350. Toutefois, cette fois, l'exemple s'enregistre à 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éclenche un autre Storyboard qui anime la même propriété de sa valeur actuelle à 500.
private void StoryboardC_Completed(object sender, EventArgs e)
{
Storyboard translationAnimationStoryboard =
(Storyboard)this.Resources["TranslationAnimationStoryboardResource"];
translationAnimationStoryboard.Begin(this);
}
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
Voici le balisage qui définit la deuxième Storyboard en tant que 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 pouvez vous attendre à ce que la propriété X de l'TranslateTransform s’anime de 0 à 350, puis revenir à 0 une fois terminée (car elle a un paramètre FillBehavior de Stop), puis animer de 0 à 500. Au lieu de cela, le TranslateTransform anime de 0 à 350, puis à 500.
Cela est dû à l’ordre dans lequel WPF déclenche des événements et parce 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 (la première Storyboard). À ce stade, la propriété X retourne toujours sa valeur animée, car elle n’a pas encore été invalidée. La deuxième Storyboard utilise la valeur mise en cache comme valeur de départ et commence à animer.
Performance
Les animations continuent à s’exécuter après avoir navigué loin d’une page
Lorsque vous quittez un Page qui contient des animations en cours d’exécution, ces animations continueront à s’exécuter jusqu’à ce que le Page soit collecté par le ramasse-miettes. Selon le système de navigation que vous utilisez, une page que vous quittez peut rester en mémoire pendant une durée indéfinie, tout en consommant des ressources avec ses animations. Cela est le plus notable lorsqu’une page contient des animations en cours d’exécution constante (« ambiante »).
Pour cette raison, il est judicieux d’utiliser l’événement Unloaded pour supprimer des animations lorsque vous quittez une page.
Il existe différentes façons de supprimer une animation. Les techniques suivantes peuvent être utilisées pour supprimer des animations appartenant à un Storyboard.
Pour supprimer un Storyboard que vous avez démarré avec un déclencheur d'événement, consultez Comment : Supprimer un storyboard.
Pour utiliser du code pour supprimer un Storyboard, consultez la méthode Remove.
La technique suivante peut être utilisée indépendamment de 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é à animer comme premier paramètre, et
null
comme deuxième. Cela supprime toutes les horloges d’animation de la propriété.
Pour plus d’informations sur les différentes façons 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, AnimationTimelineou AnimationClock à une propriété à l’aide du ComposeHandoffBehavior, tous les objets Clock précédemment associés à cette propriété continuent à consommer des ressources système ; le système de minutage ne supprime pas automatiquement ces horloges.
Pour éviter les problèmes de performances lorsque vous appliquez un grand nombre d’horloges à l’aide de Compose, vous devez supprimer les horloges composantes de la propriété animée après leur achèvement. Il existe plusieurs façons de supprimer une horloge.
Pour supprimer toutes les horloges d’une propriété, utilisez la méthode ApplyAnimationClock(DependencyProperty, AnimationClock) ou BeginAnimation(DependencyProperty, AnimationTimeline) de l’objet animé. 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 supprimer un AnimationClock spécifique d’une liste d’horloges, utilisez la propriété Controller de l'AnimationClock pour récupérer un ClockController, puis appelez la méthode Remove du ClockController. Cette opération est généralement effectuée dans le gestionnaire d’événements Completed pour une horloge. Notez que seules les horloges racines peuvent être contrôlées par un ClockController; la propriété Controller d'une horloge enfant renverra
null
. Notez également que l’événement de Completed ne sera pas appelé si la durée effective de l’horloge est perpétuelle. Dans ce cas, l’utilisateur doit déterminer quand appeler Remove.
Il s’agit principalement d’un problème pour les animations sur les objets qui ont une longue durée de vie. Lorsqu’un objet est nettoyé par le ramasse-miettes, ses horloges sont également déconnectées et nettoyées par le ramasse-miettes.
Pour plus d’informations sur les objets horloge, consultez Vue d’ensemble de l’animation et du système de synchronisation.
Voir aussi
.NET Desktop feedback