Dicas e truques de animação
Ao trabalhar com animações no WPF, há uma série de dicas e truques que podem fazer com que suas animações tenham um desempenho melhor e poupem frustração.
Problemas gerais
Animar a posição de uma barra de rolagem ou controle deslizante os deixa congelados
Se você animar a posição de uma barra de rolagem ou controle deslizante usando uma animação que tenha um FillBehavior de (o valor padrão), o usuário não poderá mais mover a barra de HoldEnd rolagem ou o controle deslizante. Isso acontece porque, mesmo que a animação tenha terminado, ela ainda estará substituindo o valor base da propriedade de destino. Para impedir que a animação substitua o valor atual da propriedade, remova-a ou atribua-lhe um FillBehavior de Stop. Para obter mais informações e um exemplo, consulte Definir uma propriedade após animá-la com um storyboard.
Animar a saída de uma animação não tem nenhum efeito
Você não pode animar um objeto que é a saída de outra animação. Por exemplo, se você usar um para animar o Fill de um de a RadialGradientBrush para um SolidColorBrushObjectAnimationUsingKeyFramesRectangle , não será possível animar nenhuma propriedade do RadialGradientBrush ou SolidColorBrush.
Não é possível alterar o valor de uma propriedade após animá-la
Em alguns casos, pode parecer que não é possível alterar o valor de uma propriedade depois de ela ter sido animada, mesmo após o término da animação. Isso acontece porque, mesmo que a animação tenha terminado, ela ainda estará substituindo o valor base da propriedade. Para impedir que a animação substitua o valor atual da propriedade, remova-a ou atribua-lhe um FillBehavior de Stop. Para obter mais informações e um exemplo, consulte Definir uma propriedade após animá-la com um storyboard.
Alterar uma linha do tempo não tem nenhum efeito
Embora a maioria das Timeline propriedades seja animável e possa ser vinculada a dados, alterar os valores de propriedade de um ativo Timeline parece não ter efeito. Isso porque, quando um é iniciado, o sistema de temporização faz uma cópia do Timeline e o usa para criar um TimelineClock objeto. Modificar o original não causa nenhum efeito na cópia do sistema.
Para que um Timeline reflita as mudanças, seu relógio deve ser regenerado e usado para substituir o relógio criado anteriormente. Os relógios não são regenerados automaticamente para você. A seguir estão várias maneiras para aplicar alterações de linha do tempo:
Se a linha do tempo for ou pertencer a um , você poderá fazê-la refletir as alterações reaplicando seu storyboard usando um StoryboardBeginStoryboard ou o Begin método. Isso tem o efeito colateral de também reiniciar a animação. No código, você pode usar o método para avançar o Seek storyboard de volta à sua posição anterior.
Se você aplicou uma animação diretamente a uma propriedade usando o método, chame o BeginAnimationBeginAnimation método novamente e passe-lhe a animação que foi modificada.
Se você estiver trabalhando diretamente no nível do relógio, crie e aplique um novo conjunto de relógios e use-os para substituir o conjunto anterior de relógios gerados.
Para obter mais informações sobre linhas do tempo e relógios, consulte Visão geral da animação e do sistema de tempo.
O FillBehavior.Stop não funciona como o esperado
Há momentos em que definir a FillBehavior propriedade para parece não ter efeito, como quando uma animação "passa" para Stop outra porque tem uma HandoffBehavior configuração de SnapshotAndReplace.
O exemplo a seguir cria um , a Rectangle e um .CanvasTranslateTransform O TranslateTransform será animado para movimentar o CanvasRectangle .
<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>
Os exemplos nesta seção usam os objetos anteriores para demonstrar vários casos em que a FillBehavior propriedade não se comporta como você poderia esperar.
FillBehavior="Stop" and HandoffBehavior com várias animações
Às vezes, parece que uma animação ignora sua FillBehavior propriedade quando é substituída por uma segunda animação. Veja o exemplo a seguir, que cria dois Storyboard objetos e os usa para animar o mesmo TranslateTransform mostrado no exemplo anterior.
O primeiro Storyboard, , anima a XTranslateTransform propriedade do de 0 a 350, B1
que move o retângulo de 350 pixels para a direita. Quando a animação atinge o final de sua duração e pára de ser reproduzida, a X propriedade reverte para seu valor original, 0. Como resultado, o retângulo move-se para a direita em 350 pixels e, em seguida, retorna para a sua posição original.
<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>
O segundo Storyboard, , B2
também anima a X propriedade do mesmo TranslateTransform. Como somente a To propriedade da animação é Storyboard definida, a animação usa o valor atual da propriedade que anima como seu valor inicial.
<!-- 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>
Se você clicar no segundo botão enquanto o primeiro Storyboard está sendo reproduzido, você pode esperar o seguinte comportamento:
O primeiro storyboard termina e envia o retângulo de volta à sua posição original, porque a animação tem um FillBehavior de Stop.
O segundo storyboard entra em vigor e anima, da posição atual, que agora é 0, até a posição 500.
Mas não isso o que acontece. Em vez disso, o retângulo não retorna, mas continua se movendo para a direita. Isso acontece porque a segunda animação usa o valor atual da primeira animação como seu valor inicial e anima desse valor até o 500. Quando a segunda animação substitui a primeira porque o SnapshotAndReplaceHandoffBehavior é usado, o FillBehavior da primeira animação não importa.
FillBehavior e o evento concluído
Os exemplos a seguir demonstram outro cenário em que o StopFillBehavior parece não surtir efeito. Novamente, o exemplo usa um Storyboard para animar a propriedade do TranslateTransform de 0 a X 350. No entanto, desta vez o exemplo registra para o Completed evento.
<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>
O Completed manipulador de eventos inicia outro Storyboard que anima a mesma propriedade de seu valor atual para 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
A seguir está a marcação que define o segundo Storyboard como um recurso.
<Page.Resources>
<Storyboard x:Key="TranslationAnimationStoryboardResource">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
To="500" Duration="0:0:5" />
</Storyboard>
</Page.Resources>
Ao executar o Storyboard, você pode esperar que a XTranslateTransform propriedade do anime de 0 a 350, reverta para 0 depois de concluída (porque tem uma FillBehavior configuração de ) e, em seguida, anime de Stop0 a 500. Em vez disso, os TranslateTransform anima de 0 a 350 e depois a 500.
Isso ocorre devido à ordem em que o WPF gera eventos e porque os valores de propriedade são armazenados em cache e não são recalculados, a menos que a propriedade seja invalidada. O Completed evento é processado primeiro porque foi acionado pela linha do tempo raiz (a primeira Storyboard). Neste momento, a X propriedade ainda retorna seu valor animado porque ainda não foi invalidada. O segundo Storyboard usa o valor armazenado em cache como seu valor inicial e começa a animar.
Desempenho
As animações continuam a ser executadas depois de sair de uma página
Quando você navega para fora de um Page que contém animações em execução, essas animações continuarão a ser reproduzidas até que o Page lixo seja coletado. Dependendo do sistema de navegação que você está usando, uma página da qual você sai talvez continue na memória por um tempo indefinido, consumindo recursos com as animações. Isso é mais perceptível quando uma página contém animações em constante execução ("ambiente").
Por esse motivo, é recomendável usar o Unloaded evento para remover animações quando você sair de uma página.
Há diferentes maneiras de remover uma animação. As técnicas a seguir podem ser usadas para remover animações que pertencem a um Storyboardarquivo .
Para remover um gatilho de evento iniciado com um gatilho de evento, consulte Como remover um Storyboard storyboard.
Para usar o código para remover um Storyboard, consulte o Remove método.
A próxima técnica pode ser usada independentemente de como a animação foi iniciada.
- Para remover animações de uma propriedade específica, use o BeginAnimation(DependencyProperty, AnimationTimeline) método. Especifique a propriedade que está sendo animada como o primeiro parâmetro e
null
como o segundo. Isso removerá todos os relógios de animação da propriedade.
Para obter mais informações sobre as diferentes maneiras para animar propriedades, consulte Visão geral das técnicas de animação de propriedades.
O uso do HandoffBehavior composto consome recursos do sistema
Quando você aplica um Storyboard, ou AnimationClock a uma propriedade usando o , quaisquer Clock objetos anteriormente associados a essa propriedade continuam a consumir recursos do sistema, AnimationTimelineo ComposeHandoffBehaviorsistema de temporização não removerá esses relógios automaticamente.
Para evitar problemas de desempenho ao aplicar um grande número de relógios usando Composeo , remova os relógios de composição da propriedade animada depois que eles forem concluídos. Há várias maneiras para remover um relógio.
Para remover todos os relógios de uma propriedade, use o ApplyAnimationClock(DependencyProperty, AnimationClock) método ou BeginAnimation(DependencyProperty, AnimationTimeline) do objeto animado. Especifique a propriedade que está sendo animada como o primeiro parâmetro e
null
como o segundo. Isso removerá todos os relógios de animação da propriedade.Para remover um específico AnimationClock de uma lista de relógios, use a Controller propriedade de para AnimationClock recuperar um ClockController, em seguida, chame o Remove método do ClockController. Isso geralmente é feito no manipulador de eventos de Completed um relógio. Observe que apenas os relógios raiz podem ser controlados por um ; a Controller propriedade de um ClockControllerrelógio filho retornará
null
. Observe também que o Completed evento não será chamado se a duração efetiva do relógio for para sempre. Nesse caso, o usuário precisará determinar quando chamar Removeo .
Isso é basicamente um problema para animações em objetos que têm um longo tempo de vida. Quando um objeto passa pela coleta de lixo, seus relógios também serão desconectados e coletados como lixo.
Para obter mais informações sobre objetos de relógio, consulte o Visão geral da animação e do sistema de tempo.
Confira também
.NET Desktop feedback