Partilhar via


Dicas e truques de animação

Ao trabalhar com animações no WPF, há uma série de dicas e truques que podem melhorar o desempenho de suas animações e evitar sua frustração.

Questões Gerais

Animar a posição de uma barra de rolagem ou controle deslizante congela-a

Se animares a posição de uma barra de rolagem ou de um controle deslizante usando uma animação que tenha um FillBehavior de HoldEnd (o valor padrão), o utilizador não poderá mais mover a barra de rolagem ou o controle deslizante. Isso acontece porque, ainda que a animação tenha terminado, ela continua a substituir 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 efeito

Não é possível animar um objeto que seja a saída de outra animação. Por exemplo, se você usar um ObjectAnimationUsingKeyFrames para animar o Fill de a Rectangle de a RadialGradientBrush para um SolidColorBrush, não poderá animar nenhuma propriedade do RadialGradientBrush ou SolidColorBrush.

Não é possível alterar o valor de uma propriedade depois de animá-la

Em alguns casos, pode parecer que não é possível alterar o valor de uma propriedade depois que ela é animada, mesmo depois que a animação termina. Isso porque, mesmo que a animação tenha terminado, ela ainda está sobrepondo-se ao 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 efeito

Embora a maioria das Timeline propriedades sejam animáveis e possam ser vinculadas a dados, alterar os valores de propriedade de um ativo Timeline parece não ter efeito. Isso porque, quando um Timeline é iniciado, o sistema de cronometragem faz uma cópia do Timeline e o usa para criar um Clock objeto. A modificação do original não tem efeito na cópia do sistema.

Para que um Timeline reflita as alterações, 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 si. A seguir estão várias maneiras de aplicar alterações na linha do tempo:

  • Se a linha do tempo for ou pertencer a um Storyboard, você poderá fazê-lo refletir as alterações reaplicando seu storyboard usando um BeginStoryboard ou o Begin método. Isso tem o efeito colateral de também reiniciar a animação. No código, você pode usar o Seek método para avançar o storyboard de volta à sua posição anterior.

  • Se você aplicou uma animação diretamente a uma propriedade usando o BeginAnimation método, chame o BeginAnimation 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 cronogramas e relógios, consulte Visão geral do sistema de animação e cronometragem.

FillBehavior.Stop não funciona como esperado

Há momentos em que definir a FillBehavior propriedade como Stop parece não ter efeito, como quando uma animação "passa" para outra porque tem uma HandoffBehavior configuração de SnapshotAndReplace.

O exemplo a seguir cria um Canvas, a Rectangle e um TranslateTransform. O TranslateTransform será animado para mover o Rectangle ao redor do 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>

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" e 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. Tomemos o exemplo a seguir, que cria dois Storyboard objetos e os usa para animar o mesmo TranslateTransform mostrado no exemplo anterior.

O primeiro Storyboard, B1, anima a propriedade X do TranslateTransform de 0 a 350, movendo o retângulo 350 pixels para a direita. Quando a animação atinge o final de sua duração e para de reproduzir, a X propriedade reverte para seu valor original, 0. Como resultado, o retângulo move-se 350 pixels para a direita e, em seguida, salta novamente 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 propriedade X do mesmo TranslateTransform. Como apenas a To propriedade da animação neste 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 é ativado e anima a partir da posição atual, começando em 0 até 500.

Mas não é isso que acontece. Em vez disso, o retângulo não salta para trás; continua a mover-se para a direita. Isso ocorre porque a segunda animação usa o valor atual da primeira animação como seu valor inicial e anima desse valor para 500. Quando a segunda animação substitui a primeira porque a SnapshotAndReplaceHandoffBehavior é usada, a FillBehavior da primeira animação não importa.

FillBehavior e o Evento Concluído

Os exemplos seguintes demonstram outro cenário em que o StopFillBehavior parece não surtir efeito. Novamente, o exemplo usa um Storyboard para animar a X propriedade do TranslateTransform de 0 a 350. No entanto, desta vez, o exemplo regista-se para o evento 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>

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, pode esperar que a propriedade X do TranslateTransform seja animada de 0 a 350, reverter para 0 após a conclusão (já que tem uma configuração FillBehavior de Stop) e, em seguida, seja animada de 0 a 500. Em vez disso, o TranslateTransform move-se de 0 para 350 e depois para 500.

Isso ocorre devido à ordem na qual 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 animação.

Desempenho

As animações continuam a ser executadas depois de navegar para fora de uma página

Quando navega para longe de um Page que contém animações em execução, essas animações continuarão a ser executadas até que o Page seja coletado pelo coletor de lixo. Dependendo do sistema de navegação que está a utilizar, uma página de que se afastou pode permanecer na memória por um período indeterminado, ao consumir recursos com as suas animações. Isso é mais percetível quando uma página contém animações ("ambiente") em execução constante.

Por esse motivo, é uma boa ideia usar o Unloaded evento para remover animações quando você navega para fora 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 de animar propriedades, consulte Visão geral das técnicas de animação de propriedade.

Usar o Compose HandoffBehavior consome recursos do sistema

Quando você aplica um Storyboard, AnimationTimeline, ou AnimationClock a uma propriedade usando o ComposeHandoffBehavior, quaisquer Clock objetos anteriormente associados a essa propriedade continuam a consumir recursos do sistema, o sistema 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 , você deve remover os relógios de composição da propriedade animada depois que eles forem concluídos. Existem várias maneiras de remover um relógio.

Isso é principalmente um problema para animações em objetos que têm uma longa vida útil. Quando um objeto é recolhido pelo coletor de lixo, os seus relógios também serão desconectados e recolhidos pelo coletor de lixo.

Para obter mais informações sobre objetos de relógio, consulte Visão geral do sistema de animação e cronometragem.

Ver também

  • Visão geral da animação