Animaciones de fotograma clave y animaciones de función de aceleración

Las animaciones de fotograma clave lineales, las animaciones de fotograma clave con un valor KeySpline o las funciones de aceleración son tres técnicas distintas para prácticamente el mismo escenario: crear una animación de guion gráfico que es un poco más compleja y que tiene un comportamiento de animación no lineal desde un estado inicial hasta un estado final.

Requisitos previos

Asegúrate de que has leído el tema sobre las animaciones de guion gráfico. Este tema se basa en los conceptos de animación que se explicaron en el tema sobre animaciones de guion gráfico y no los explicaremos nuevamente. Por ejemplo, en la sección de animaciones de guion gráfico se describe cómo seleccionar como destino animaciones y guiones gráficos para usarlos como recursos, los valores de la propiedad Timeline tales como Duration, FillBehavior, etc.

Animación mediante animaciones de fotograma clave

Las animaciones de fotograma clave permiten que se alcance más de un valor de destino en un punto junto con la escala de tiempo de la animación. En otras palabras, cada fotograma clave puede especificar un valor intermedio diferente y el último fotograma clave alcanzado es el valor de animación final. Si especificas varios valores para animar, puedes crear animaciones más complejas. Las animaciones de fotograma clave también permiten una lógica de interpolación distinta, las cuales se implementan como una subclase KeyFrame diferente según el tipo de animación. Específicamente, cada tipo de animación de fotograma clave presenta una variación de Discrete, Linear, Spline y Easing de su clase KeyFrame para especificar sus fotogramas clave. Por ejemplo, para especificar una animación que selecciona como destino Double y usa fotogramas clave, puedes declarar fotogramas clave con DiscreteDoubleKeyFrame, LinearDoubleKeyFrame, SplineDoubleKeyFrame y EasingDoubleKeyFrame. Puedes usar algunos de estos tipos, o todos, dentro de una única colección de KeyFrames, para cambiar la interpolación cada vez que se alcanza un nuevo fotograma clave.

En el caso del comportamiento de interpolación, cada fotograma clave controla la interpolación hasta que se alcanza el tiempo KeyTime. En ese momento también se alcanza su Value. Si hay más fotogramas clave detrás, el valor se convierte entonces en el valor inicial para el siguiente fotograma clave de una secuencia.

Al inicio de la animación, si no existe ningún fotograma clave con un elemento KeyTime con el valor "0:0:0", el valor inicial es igual al valor no animado de la propiedad. Esto es similar a cómo una animación De/a/por actúa si no hay ningún From.

De forma implícita, la duración de una animación de fotograma clave es la duración del valor más alto de KeyTime establecido en cualquiera de los fotogramas clave. Si quieres, puedes configurar un valor de Duration explícito, pero recuerda que no puede ser menor que el valor KeyTime de tus propios fotogramas clave; de lo contrario, cortarás parte de la animación.

Además de Duration, puedes establecer todas las propiedades de escala de tiempo basadas en una animación de fotograma clave, como puedes con una animación From/To/By , ya que las clases de animación de fotograma clave también derivan de Timeline. Son las siguientes:

  • AutoReverse: una vez que se alcanza el último fotograma clave, los fotogramas se repiten en orden inverso desde el final. Esto duplica la duración aparente de la animación.
  • BeginTime: retrasa el inicio de la animación. La escala de tiempo para los valores KeyTime de los fotogramas no inicia el recuento hasta que se alcanza BeginTime, de modo que no hay riesgo de cortar los fotogramas.
  • FillBehavior: controla lo que sucede cuando se llega al último fotograma clave. FillBehavior no tiene ningún efecto en los fotogramas clave intermedios.
  • RepeatBehavior:
    • si está establecido como Forever, los fotogramas clave y sus escalas de tiempo se repiten de manera indefinida.
    • Si está establecido como un recuento de iteraciones, la escala de tiempo se repite esa cantidad de veces.
    • Si está establecido como un valor de Duration, la escala de tiempo se repite hasta llegar a dicho horario. Esto puede truncar parte de la animación en la secuencia de fotograma clave, si no es un factor entero de la duración implícita de la escala de tiempo.
  • SpeedRatio (no se usa con frecuencia)

Fotogramas clave lineales

Los fotogramas clave lineales dan como resultado una interpolación lineal simple del valor hasta que se alcanza el valor KeyTime del fotograma. Este comportamiento de interpolación es el más similar al más sencillo de las animaciones From/To/By descritas en el tema Animaciones con guion gráfico .

Aquí te mostramos cómo usar una animación de fotograma clave para escalar el alto de un rectángulo con fotogramas clave lineales. Este ejemplo ejecuta una animación en la que el alto del rectángulo aumenta levemente, de manera lineal, durante los primeros cuatro segundos y, luego, escala rápidamente hasta el último segundo hasta que el rectángulo presenta el doble del alto inicial.

<StackPanel>
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimationUsingKeyFrames
              Storyboard.TargetName="myRectangle"
              Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <LinearDoubleKeyFrame Value="1" KeyTime="0:0:0"/>
                <LinearDoubleKeyFrame Value="1.2" KeyTime="0:0:4"/>
                <LinearDoubleKeyFrame Value="2" KeyTime="0:0:5"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </StackPanel.Resources>
</StackPanel>

Fotogramas clave discretos

Los fotogramas clave discretos no usan ninguna interpolación. Cuando se llega a un valor KeyTime, simplemente se aplica el nuevo Value. Según la propiedad de interfaz de usuario que se está animando, por lo general, esto genera una animación que simula un "salto". Asegúrate de que este sea el comportamiento estético que realmente quieres. Puedes minimizar los saltos aparentes al aumentar la cantidad de fotogramas clave declarados, pero si tu objetivo es una animación suave, lo mejor es que uses en cambio fotogramas clave lineales o spline.

Nota

Los fotogramas clave discretos son la única manera de animar un valor que no sea de tipo Double, Point y Color, con un DiscreteObjectKeyFrame. Analizaremos esto en mayor detalle más adelante en este tema.

Fotogramas clave spline

Un fotograma clave spline crea una transición variable entre valores según el valor de la propiedad KeySpline . Esta propiedad especifica el primer y el segundo punto de control de una curva Bézier, que describen la aceleración de la animación. Básicamente, una keySpline define una relación de función a lo largo del tiempo donde el gráfico en tiempo de función es la forma de esa curva Bezier. Normalmente, se especifica un valor KeySpline en una cadena de atributo abreviado XAML que tiene cuatro valores Double separados por espacios o comas. Estos valores son pares "X,Y" para dos puntos de control de la curva Bézier. X representa al tiempo e Y es el modificador de función para el valor. Cada valor debe estar comprendido siempre entre 0 y 1, ambos inclusive. Sin una modificación del punto de control a un valor KeySpline, la línea recta de 0,0 a 1,1 es la representación de una función a lo largo del tiempo para una interpolación lineal. Los puntos de control cambian la forma de la curva y, por ende, el comportamiento de la función a lo largo del tiempo para la animación spline. Lo mejor es verlo en un gráfico. Puedes ejecutar la muestra del visualizador de spline clave de Silverlight en un explorador para ver de qué manera los puntos de control modifican la curva y cómo se ejecuta una animación de muestra cuando se la usa como un valor KeySpline.

El ejemplo que sigue muestra tres fotogramas clave diferentes aplicados a una animación. El último es una animación spline clave para un valor Double (SplineDoubleKeyFrame). Observa la cadena "0.6,0.0 0.9,0.00" que se aplica a KeySpline. Esta produce una curva en la que la animación parece ejecutarse lentamente al principio, pero luego alcanza con velocidad el valor justo antes de que se llegue KeyTime.

<Storyboard x:Name="myStoryboard">
    <!-- Animate the TranslateTransform's X property
        from 0 to 350, then 50,
        then 200 over 10 seconds. -->
    <DoubleAnimationUsingKeyFrames
        Storyboard.TargetName="MyAnimatedTranslateTransform"
        Storyboard.TargetProperty="X"
        Duration="0:0:10" EnableDependentAnimation="True">

        <!-- Using a LinearDoubleKeyFrame, the rectangle moves 
            steadily from its starting position to 500 over 
            the first 3 seconds.  -->
        <LinearDoubleKeyFrame Value="500" KeyTime="0:0:3"/>

        <!-- Using a DiscreteDoubleKeyFrame, the rectangle suddenly 
            appears at 400 after the fourth second of the animation. -->
        <DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4"/>

        <!-- Using a SplineDoubleKeyFrame, the rectangle moves 
            back to its starting point. The
            animation starts out slowly at first and then speeds up. 
            This KeyFrame ends after the 6th second. -->
        <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

Fotogramas clave de aceleración

Un fotograma clave de aceleración es un fotograma clave en el que se aplica la interpolación y la función a lo largo del tiempo de la interpolación se controla mediante varias fórmulas matemáticas predefinidas. En realidad, puede producir mucho el mismo resultado con un fotograma clave spline que con algunos de los tipos de función de aceleración, pero también hay algunas funciones de aceleración, como BackEase, que no se pueden reproducir con una spline.

Para aplicar una función de aceleración a un fotograma clave de aceleración, debes establecer la propiedad EasingFunction como un elemento de propiedad en XAML para dicho fotograma clave. Para el valor, especifica un elemento de objeto para uno de los tipos de funciones de aceleración.

En este ejemplo se aplica CubicEase y, luego, BounceEase como fotogramas clave sucesivos a DoubleAnimation para crear un efecto de rebote.

<Storyboard x:Name="myStoryboard">
    <DoubleAnimationUsingKeyFrames Duration="0:0:10"
        Storyboard.TargetProperty="Height"
        Storyboard.TargetName="myEllipse">

        <!-- This keyframe animates the ellipse up to the crest 
            where it slows down and stops. -->
        <EasingDoubleKeyFrame Value="-300" KeyTime="00:00:02">
            <EasingDoubleKeyFrame.EasingFunction>
                <CubicEase/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>

        <!-- This keyframe animates the ellipse back down and makes
            it bounce. -->
        <EasingDoubleKeyFrame Value="0" KeyTime="00:00:06">
            <EasingDoubleKeyFrame.EasingFunction>
                <BounceEase Bounces="5"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

Este es solo un ejemplo de una función de aceleración. Podrás encontrar más información en la siguiente sección.

Funciones de aceleración

Las funciones de aceleración le permiten aplicar fórmulas matemáticas personalizadas a las animaciones. Por lo general, las operaciones matemáticas resultan útiles para producir animaciones que simulan una física del mundo real en un sistema de coordenadas en 2D. Por ejemplo, puede que quiera que un objeto rebote de forma realista o se comporte como si estuviera sobre un muelle. Podrías usar fotograma clave o incluso animaciones de From/ ToBy paraaproximar/ estos efectos, pero tomaría una cantidad significativa de trabajo y la animación sería menos precisa que usar una fórmula matemática.

Las funciones de aceleración se pueden aplicar a las animaciones de tres formas:

A continuación te indicamos una lista de funciones de aceleración:

  • BackEase: retira el movimiento de una animación un poco antes de que comience dicha animación en la ruta indicada.
  • BounceEase: crea un efecto de rebote.
  • CircleEase: crea una animación que se acelera o desacelera con una función circular.
  • CubicEase: crea una animación que se acelera o desacelera con la fórmula f(t) = t3.
  • ElasticEase: crea una animación que se asemeja a un muelle que oscila de arriba abajo hasta que se detiene.
  • ExponentialEase: crea una animación que se acelera o desacelera con una fórmula exponencial.
  • PowerEase: crea una animación que se acelera o desacelera con la fórmula f(t) = tp, donde p equivale a la propiedad Power.
  • QuadraticEase: crea una animación que se acelera o desacelera con la fórmula f(t) = t2.
  • QuarticEase: crea una animación que se acelera o desacelera con la fórmula f(t) = t4.
  • QuinticEase: crea una animación que se acelera o desacelera con la fórmula f(t) = t5.
  • SineEase: crea una animación que se acelera o desacelera con una fórmula senoidal.

Algunas de las funciones de aceleración tienen sus propias propiedades. Por ejemplo, BounceEase tiene dos propiedades Bounces y Bounciness que modifican el comportamiento a lo largo del tiempo de dicha función BounceEase. Otras funciones de aceleración, como CubicEase, no tienen otras propiedades que no sean la propiedad EasingMode que comparten todas las funciones de aceleración y siempre producen el mismo comportamiento de la función a lo largo del tiempo.

Algunas de estas funciones de aceleración se superponen un poco, según cómo configures las propiedades en las funciones de aceleración que contienen propiedades. Por ejemplo, QuadraticEase es exactamente igual que PowerEase con un valor de Power que equivale a 2. Y CircleEase es básicamente un valor predeterminado ExponentialEase.

La función de aceleración BackEase es única porque puede cambiar el valor fuera del intervalo normal establecido por From/To o valores de fotogramas clave. Inicia la animación cambiando el valor en la dirección opuesta como se esperaría desde un comportamiento normal de From/To , vuelve al valor From o starting de nuevo y, a continuación, ejecuta la animación como normal.

En un ejemplo anterior, mostramos cómo declarar una función de aceleración para una animación de fotograma clave. En este ejemplo siguiente se aplica una función de aceleración a una animación De/a/por .

<StackPanel x:Name="LayoutRoot" Background="White">
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimation From="30" To="200" Duration="00:00:3" 
                Storyboard.TargetName="myRectangle" 
                Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <DoubleAnimation.EasingFunction>
                    <BounceEase Bounces="2" EasingMode="EaseOut" 
                                Bounciness="2"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
    </StackPanel.Resources>
    <Rectangle x:Name="myRectangle" Fill="Blue" Width="200" Height="30"/>
</StackPanel>

Cuando se aplica una función de aceleración a una animación From/To/By , cambia las características de función a lo largo del tiempo de cómo el valor interpola entre los valores From y To durante la duración de la animación. Sin una función de aceleración, se trataría de una interpolación lineal.

Animaciones discretas de valor de objeto

Hay un tipo de animación que merece especial atención porque es la única forma en la que se puede aplicar un valor animado a las propiedades que no son del tipo Double, Point o Color. Se trata de la animación de fotograma clave ObjectAnimationUsingKeyFrames. La animación con valores Object es diferente porque no es posible interpolar los valores entre los fotogramas. Cuando se alcanza el valor KeyTime del fotograma, el valor animado se establece inmediatamente en el valor especificado en la propiedad Value en el fotograma clave. Dado que no hay interpolación, solo hay un fotograma clave que se usa en la colección de fotogramas clave ObjectAnimationUsingKeyFrames : DiscreteObjectKeyFrame.

Por lo general, la propiedad Value de la clase DiscreteObjectKeyFrame se establece con una sintaxis de elementos de propiedades, ya que el valor del objeto que intentas establecer a menudo no se expresa como una cadena para rellenar la propiedad Value en una sintaxis de atributos. Todavía puede usar la sintaxis de atributo si usa una referencia como StaticResource.

Verás que se usa la clase ObjectAnimationUsingKeyFrames en las plantillas predeterminadas, cuando una propiedad de la plantilla hace referencia a un recurso Brush. Estos recursos son objetos SolidColorBrush, no solo un valor Color, y usan recursos definidos como temas del sistema (ThemeDictionaries). Se pueden asignar directamente a un valor del tipo Brush como TextBlock.Foreground y no necesitan usar selección indirecta. Pero, dado que SolidColorBrush no es Double, Point ni Color, debes usar ObjectAnimationUsingKeyFrames para poder usar el recurso.

<Style x:Key="TextButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid Background="Transparent">
                    <TextBlock x:Name="Text"
                        Text="{TemplateBinding Content}"/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
...
                       </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

También puedes usar ObjectAnimationUsingKeyFrames para animar propiedades que usan un valor de enumeración. Aquí tienes otro ejemplo de un estilo con nombre procedente de las plantillas predeterminadas de Windows Runtime. Observa que se establece la propiedad Visibility que toma una constante de enumeración de Visibility. En este caso, puedes establecer el valor con la sintaxis de atributos. Solo necesitas el nombre de constante no completo de una enumeración para establecer una propiedad con un valor de enumeración, por ejemplo, "Collapsed".

<Style x:Key="BackButtonStyle" TargetType="Button">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">
          <Grid x:Name="RootGrid">
            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CommonStates">
              <VisualState x:Name="Normal"/>
...           <VisualState x:Name="Disabled">
                <Storyboard>
                  <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame Value="Collapsed" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
...
          </VisualStateManager.VisualStateGroups>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Puedes usar más de un DiscreteObjectKeyFrame para un conjunto de fotogramas ObjectAnimationUsingKeyFrames. Esta puede ser una forma interesante de crear una animación de presentación al animar el valor de Image.Source, como un escenario de ejemplo para el cual varios valores de objetos pueden resultar útiles.