Compartir a través de


Sugerencias y trucos de animación

Al trabajar con animaciones en WPF, hay una serie de consejos y trucos que pueden hacer que las animaciones funcionen mejor y te ahorran frustración.

Problemas generales

Animar la posición de una barra de desplazamiento o control deslizante la inmoviliza

Si animas la posición de una barra de desplazamiento o control deslizante mediante una animación que tiene un FillBehavior de HoldEnd (el valor predeterminado), el usuario ya no podrá mover la barra de desplazamiento o el control deslizante. Esto se debe a que, aunque la animación finalizó, continúa sobrescribiendo el valor base de la propiedad de destino. Para evitar que la animación sobrescriba el valor actual de la propiedad, quítelo o asígnele un valor de FillBehavior a Stop. Para obtener más información y un ejemplo, vea Establecer una propiedad después de animarla con un guión gráfico.

Animar la salida de una animación no tiene ningún efecto

No se puede animar un objeto que sea la salida de otra animación. Por ejemplo, si usa un ObjectAnimationUsingKeyFrames para animar el Fill de un Rectangle de un RadialGradientBrush a un SolidColorBrush, no puede animar ninguna propiedad del RadialGradientBrush o del SolidColorBrush.

No se puede cambiar el valor de una propiedad después de animarla.

En algunos casos, puede parecer que no se puede cambiar el valor de una propiedad después de que se haya animado, incluso después de que la animación haya finalizado. Esto se debe a que, aunque la animación finalizó, sigue reemplazando el valor base de la propiedad. Para evitar que la animación sobrescriba el valor actual de la propiedad, quítelo o asígnele un valor de FillBehavior a Stop. Para obtener más información y un ejemplo, vea Establecer una propiedad después de animarla con un guión gráfico.

Cambiar una escala de tiempo no tiene ningún efecto

Aunque la mayoría Timeline de las propiedades son animables y pueden enlazarse a datos, cambiar los valores de propiedad de un activo Timeline parece no tener ningún efecto. Esto se debe a que, cuando se inicia un Timeline , el sistema de control de tiempo realiza una copia de Timeline y lo usa para crear un Clock objeto . La modificación del original no tiene ningún efecto en la copia del sistema.

Para que un Timeline objeto refleje los cambios, su reloj debe volver a generarse y usarse para reemplazar el reloj creado anteriormente. Los relojes no se regeneran automáticamente. A continuación se muestran varias maneras de aplicar los cambios de escala de tiempo:

  • Si la línea de tiempo es o pertenece a un Storyboard, puede hacer que refleje los cambios volviendo a aplicar su guión gráfico mediante un BeginStoryboard o los métodos Begin. Esto tiene el efecto secundario de reiniciar también la animación. En el código, puede usar el Seek método para avanzar el guión gráfico de nuevo a su posición anterior.

  • Si ha aplicado una animación directamente a una propiedad mediante el BeginAnimation método , llame al BeginAnimation método de nuevo y pásela la animación que se ha modificado.

  • Si trabaja directamente a nivel de reloj, cree y aplique un nuevo conjunto de relojes y úselos para reemplazar el conjunto de relojes generados anterior.

Para obtener más información sobre las escalas de tiempo y los relojes, vea Información general sobre el sistema de tiempo y animación.

FillBehavior.Stop no funciona según lo esperado

Hay ocasiones en las que configurar la propiedad FillBehavior a Stop parece no tener ningún efecto, como cuando una animación cede el control a otra porque tiene un valor HandoffBehavior de SnapshotAndReplace.

En el ejemplo siguiente se crea un Canvas, un Rectangle y un TranslateTransform. Se animará el TranslateTransform para mover el Rectangle alrededor del 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>

En los ejemplos de esta sección se usan los objetos anteriores para mostrar varios casos en los que la FillBehavior propiedad no se comporta como podría esperar.

FillBehavior="Stop" y HandoffBehavior con varias animaciones

A veces parece que una animación omite su FillBehavior propiedad cuando se reemplaza por una segunda animación. Tome el ejemplo siguiente, que crea dos Storyboard objetos y los usa para animar lo mismo TranslateTransform que se muestra en el ejemplo anterior.

El primer Storyboard, B1 anima la propiedad X del TranslateTransform de 0 a 350, lo que mueve el rectángulo 350 píxeles a la derecha. Cuando la animación alcanza el final de su duración y deja de reproducirse, la X propiedad vuelve a su valor original, 0. Como resultado, el rectángulo se mueve a la derecha de 350 píxeles y, a continuación, vuelve a su posición 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>

El segundo Storyboard, B2, anima también la propiedad X del mismo TranslateTransform. Dado que solo se establece la propiedad de la animación en este To, la animación usa el valor actual de la propiedad que anima como su 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>

Si hace clic en el segundo botón mientras se reproduce el primero Storyboard , puede esperar el siguiente comportamiento:

  1. El primer guión gráfico termina y envía el rectángulo de vuelta a su posición original, porque la animación tiene un FillBehavior de Stop.

  2. El segundo guión gráfico surte efecto y anima desde la posición actual, que ahora es de 0 a 500.

Pero eso no es lo que sucede. En su lugar, el rectángulo no salta hacia atrás; sigue moviéndose a la derecha. Esto se debe a que la segunda animación usa el valor actual de la primera animación como su valor inicial y anima desde ese valor a 500. Cuando la segunda animación reemplaza a la primera porque el SnapshotAndReplaceHandoffBehavior se usa, el FillBehavior de la primera animación no importa.

FillBehavior y el evento completado

En los siguientes ejemplos se demuestra otro escenario donde el StopFillBehavior parece no tener ningún efecto. Otra vez, en el ejemplo se usa un Storyboard para animar la propiedad X de TranslateTransform desde 0 a 350. Sin embargo, esta vez el ejemplo se registra en el 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>

El Completed controlador de eventos inicia otro Storyboard que anima la misma propiedad desde su valor actual hasta 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 continuación se muestra el marcado que define el segundo Storyboard como un recurso.

<Page.Resources>
  <Storyboard x:Key="TranslationAnimationStoryboardResource">
    <DoubleAnimation 
      Storyboard.TargetName="MyTranslateTransform"
      Storyboard.TargetProperty="X"
      To="500" Duration="0:0:5" />
  </Storyboard>
</Page.Resources>

Al ejecutar el Storyboard, es posible que espere que la propiedad del objeto X se anime de 0 a 350, luego vuelva a 0 una vez completado (porque tiene un valor de TranslateTransform de FillBehavior), y luego se anime de 0 a 500. En su lugar, TranslateTransform se anima de 0 a 350 y luego a 500.

Esto se debe al orden en que WPF genera eventos y porque los valores de propiedad se almacenan en caché y no se recalculan a menos que la propiedad se invalide. El Completed evento se procesa primero porque lo desencadenó la escala de tiempo raíz (la primera Storyboard). En este momento, la X propiedad sigue devolviendo su valor animado porque aún no se ha invalidado. El segundo Storyboard usa el valor almacenado en caché como valor inicial y comienza a animarse.

Rendimiento

Las animaciones continúan ejecutándose después de salir de una página

Cuando se aleja de un Page que contiene animaciones en ejecución, esas animaciones seguirán reproduciéndose hasta que el Page sea recolectado por el recolector de basura. Dependiendo del sistema de navegación que use, una página desde la que navega puede permanecer en memoria durante un período indefinido de tiempo, todo mientras consume recursos con sus animaciones. Esto es más notable cuando una página contiene animaciones que se ejecutan constantemente ("de fondo").

Por este motivo, es una buena idea usar el Unloaded evento para quitar animaciones al alejarse de una página.

Hay diferentes maneras de quitar una animación. Las técnicas siguientes se pueden usar para quitar animaciones que pertenecen a Storyboard.

La siguiente técnica se puede usar independientemente de cómo se inició la animación.

  • Para quitar animaciones de una propiedad específica, use el BeginAnimation(DependencyProperty, AnimationTimeline) método . Especifique la propiedad que se está animando como primer parámetro y null como segundo. Esto quitará todos los relojes de animación de la propiedad .

Para obtener más información sobre las distintas formas de animar las propiedades, vea Información general sobre técnicas de animación de propiedades.

El uso de Compose HandoffBehavior consume recursos del sistema

Cuando se aplica un Storyboard, AnimationTimelineo AnimationClock a una propiedad mediante ComposeHandoffBehavior, los Clock objetos asociados previamente a esa propiedad siguen usando recursos del sistema; el sistema de control de tiempo no quitará estos relojes automáticamente.

Para evitar problemas de rendimiento al aplicar un gran número de relojes mediante Compose, debe eliminar los relojes componedores de la propiedad animada una vez que hayan sido completados. Hay varias maneras de quitar un reloj.

Esto es principalmente un problema para animaciones en objetos que tienen una larga duración. Cuando se recolecte un objeto como basura, sus relojes también se desconectarán y se recolectarán como basura.

Para obtener más información sobre los objetos de reloj, vea Animation and Timing System Overview.

Consulte también

  • Visión general de animación