Compartilhar via


Dicas e truques de animação

Quando estiver trabalhando com animações no WPF, há várias dicas e truques que podem melhorar o desempenho das animações e lhe poupar frustração.

Problemas gerais

Animando a posição de uma barra de rolagem ou controle deslizante Freezes It

Ao animar a posição de uma barra de rolagem ou controle deslizante usando uma animação que tem um FillBehavior de HoldEnd (o valor padrão), o usuário não será mais capaz de mover o controle deslizante ou barra de rolagem. Isso acontece porque, mesmo que a animação esteja finalizada, ela ainda está substituindo o valor base da propriedade de destino. Para impedir que a animação substitua o valor atual da propriedade, remova-a, ou dê a ela um FillBehavior de Stop. Para mais informações e um exemplo, consulte Como: Definir uma Propriedade Após Animá-la com um Storyboard.

Animando a saída de uma animação com nenhum efeito

Não é possível animar um objeto que é a saída de outra animação. Por exemplo, ao usar um ObjectAnimationUsingKeyFrames para animar o Fill de um Rectangle de um RadialGradientBrush a um SolidColorBrush, não é possível animar quaisquer propriedades do RadialGradientBrush ou SolidColorBrush.

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

Em alguns casos, pode aparecer que não se pode alterar o valor de uma propriedade após ela ter sido animada, mesmo depois que animação foi finalizada. Isso acontece porque, mesmo que a animação esteja finalizada, ela ainda está substituindo o valor base da propriedade de destino. Para impedir que a animação substitua o valor atual da propriedade, remova-a, ou dê a ela um FillBehavior de Stop. Para mais informações e um exemplo, consulte Como: Definir uma Propriedade Após Animá-la com um Storyboard.

Alterar um cronograma com nenhum efeito

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

Para um Timeline para refletir as alterações, seu relógio deve ser regenerado e usado para substituir o relógio criado anteriormente. Relógios não são regenerados automaticamente para você. A seguir estão várias maneiras para aplicar alterações no cronograma:

  • Se o cronograma for ou pertence a um Storyboard, é possível fazê-lo refletir alterações reaplicando seu storyboard usando um BeginStoryboard ou um método Begin. Isso tem o efeito lateral de também reiniciar a animação. No código, é possível usar o método Seek para reposicionar o storyboard na posição anterior.

  • Se uma animação foi aplicada diretamente a uma propriedade usando o método BeginAnimation, chame o método BeginAnimation novamente e passe-o à animação que foi modificada.

  • Se 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 de Animação e Sistema de Tempo.

FillBehavior.Stop não funciona como esperado

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

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

Os exemplos nesta seção usam os objetos anteriores para demonstrar vários casos em que a propriedade FillBehavior não se comporta como esperado.

FillBehavior="Stop" e HandoffBehavior com várias animações

Às vezes parece que embora uma animação ignore sua propriedade FillBehavior quando ela é substituída por uma segunda animação. Leve o exemplo a seguir, que cria dois objetos Storyboard e usá-los para animar o mesmo TranslateTransform mostrado no exemplo anterior.

O primeiro Storyboard, B1, anima a propriedade X do TranslateTransform de 0 a 350, o que move o retângulo 350 pixels para a direita. Quando chega ao participante da duração da animação e pára reproduzir o X propriedade será revertido para seu valor original, 0. sistema autônomo resultado, o retângulo se move para direita 350 pixels e, em seguida, vai volta para 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>

A segunda Storyboard, B2, também anima a propriedade X do mesmo TranslateTransform. Como somente a propriedade To da animação neste Storyboard está 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 clicar no segundo botão enquanto o primeiro Storyboard está tocando, pode-se esperar o seguinte comportamento:

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

  2. O segundo storyboard entra em vigor e anima a partir da posição atual, que agora é 0, a 500.

Mas não é isso que acontece. Em vez disso, o retângulo não retorna; ele continua se movendo para a direita. Isso ocorre porque a segunda animação usa o valor corrente da primeira animação sistema autônomo seu valor inicial e anima o valor a 500. When the second animation replaces the first because the SnapshotAndReplace HandoffBehavior is used, the FillBehavior of the first animation does not matter.

FillBehavior e o evento concluído

Os próximo exemplos demonstram outra situação na qual o Stop FillBehavior parece não ter nenhum efeito. Novamente, o exemplo usa um storyboard para animar o X propriedade das TranslateTransform de 0 a 350. No entanto, neste momento o exemplo registra 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 manipulador de eventos Completed inicia outro Storyboard que anima a mesma propriedade de seu valor atual até 500.

private void StoryboardC_Completed(object sender, EventArgs e)
{

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

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ê poderia esperar o X propriedade das TranslateTransform para animar de 0 a 350, em seguida, retornar a 0 após a sua conclusão (porque ele tem um FillBehavior configuração de Stop) e animar de 0 a 500. Em vez disso, a TranslateTransform anima de 0 para 350 e, em seguida, para 500.

É por causa da ordem na qual WPF gera eventos e porque valores de propriedade são armazenados em cache e não são recalculados, a menos que a propriedade seja invalidada. O evento Completed é processado primeiro porque ele foi acionado pelo cronograma raiz (o primeiro Storyboard). Nesse momento, a propriedade X ainda retorna seu valor animado porque ele ainda não foi invalidado. A segunda Storyboard usa o valor em cache como seu valor inicial e começa a animação.

Desempenho

Animações continuam a ser executadas após sair de uma página

Ao navegar para fora de uma Page que contenha animações em execução, as animações continuarão a ser executadas até que o Page seja coletado como lixo. Dependendo do sistema de navegação usado, uma página de que você navegue para fora talvez fique na memória por um período de tempo indefinido, todo o tempo consumindo recursos com suas animações. Isso é mais perceptível quando uma página contiver constantemente animações ("ambientes") em execução.

Por esse motivo, é uma boa ideia usar o evento Unloaded para remover animações quando você navegar para fora de uma página.

Há diferentes maneiras para remover uma animação. As seguintes técnicas podem ser usadas para remover as animações que pertencem a um Storyboard.

A próxima técnica pode ser usada independentemente de como a animação foi iniciada.

  • Para remover as animações de uma propriedade específica, use o método BeginAnimation(DependencyProperty, AnimationTimeline). Especifique a propriedade 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 de técnicas de animação de propriedades.

Usando a Compose HandoffBehavior consome recursos do sistema

Ao aplicar um Storyboard,AnimationTimeline, ou AnimationClock para uma propriedade usando o Compose HandoffBehavior, quaisquer objetos Clock anteriormente associados com aquela propriedade continuam a consumir recursos do sistema; o sistema de tempo não removerá esses relógios automaticamente.

Para evitar problemas de desempenho ao aplicar um grande número de relógios usando Compose, deve-se remover relógios de composição da propriedade animada depois que concluírem. Há várias maneiras para remover um relógio.

Isso é basicamente um problema para animações em objetos que possuem um longo tempo de vida. Quando um objeto é coletado como lixo, seus relógios também serão desconectados e coletados como lixo.

Para obter mais informações sobre objetos de relógios, consulte Visão Geral de Animação e Sistema de Tempo.

Consulte também

Conceitos

Revisão de Animação