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, B1que 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, , B2també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:

  1. O primeiro storyboard termina e envia o retângulo de volta à sua posição original, porque a animação tem um FillBehavior de Stop.

  2. 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 .

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.

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