Condividi tramite


Suggerimenti e consigli per l'animazione

Quando si lavora con le animazioni in WPF, ci sono diversi suggerimenti e trucchi che possono rendere le animazioni migliori e risparmiare frustrazione.

Problemi generali

Animare la posizione di una barra di scorrimento o di un cursore ne causa il blocco

Se si anima la posizione di una barra di scorrimento o di un cursore usando un'animazione con un FillBehavior di HoldEnd (valore predefinito), l'utente non potrà più muovere la barra di scorrimento o il cursore. Ciò è dovuto al fatto che, anche se l'animazione è terminata, continua comunque a sovrascrivere il valore di base della proprietà di destinazione. Per impedire all'animazione di eseguire l'override del valore corrente della proprietà, rimuoverla o assegnargli un FillBehavior di Stop. Per ulteriori informazioni e un esempio, vedere Impostare una proprietà dopo averla animata con uno storyboard.

L'animare l'output di un'animazione non produce alcun effetto

Non è possibile animare un oggetto che rappresenta l'output di un'altra animazione. Ad esempio, se si usa un ObjectAnimationUsingKeyFrames per animare il Fill di un Rectangle da un RadialGradientBrush a un SolidColorBrush, non è possibile animare alcuna proprietà del RadialGradientBrush o SolidColorBrush.

Non è possibile modificare il valore di una proprietà dopo l'animazione

In alcuni casi, potrebbe sembrare che non sia possibile modificare il valore di una proprietà dopo che è stata animata, anche dopo che l'animazione è terminata. Ciò è dovuto al fatto che, anche se l'animazione è terminata, continua comunque a sovrascrivere il valore base della proprietà. Per impedire all'animazione di eseguire l'override del valore corrente della proprietà, rimuoverla o assegnargli un FillBehavior di Stop. Per ulteriori informazioni e un esempio, vedere Impostare una proprietà dopo averla animata con uno storyboard.

La modifica di una sequenza temporale non ha alcun effetto

Anche se la maggior parte delle proprietà Timeline è animabile e può essere associata a dati, la modifica dei valori delle proprietà di un Timeline attivo sembra non avere alcun effetto. Questo perché, quando viene avviato un Timeline, il sistema di temporizzazione crea una copia del Timeline e lo usa per creare un oggetto Clock. Modificare l'originale non ha alcun effetto sulla copia del sistema.

Affinché un Timeline rifletta le modifiche, è necessario rigenerarne l'orologio e utilizzarlo per sostituire l'orologio creato in precedenza. Gli orologi non vengono rigenerati automaticamente. Di seguito sono riportati diversi modi per applicare le modifiche della sequenza temporale:

  • Se la sequenza temporale è o appartiene a un Storyboard, puoi farla riflettere i cambiamenti riapplicando lo storyboard utilizzando un BeginStoryboard o il metodo Begin. Questo ha l'effetto collaterale del riavvio dell'animazione. Nel codice è possibile usare il metodo Seek per riportare lo storyboard alla posizione precedente.

  • Se hai applicato un'animazione direttamente a una proprietà usando il metodo BeginAnimation, chiama di nuovo il metodo BeginAnimation e passa l'animazione che è stata modificata.

  • Se si lavora direttamente al livello dell'orologio, creare e applicare un nuovo insieme di orologi e usarli per sostituire l'insieme precedente di orologi generati.

Per altre informazioni sulle sequenze temporali e sugli orologi, vedere Panoramica del Sistema di Animazione e Temporizzazione.

FillBehavior.Stop non funziona come previsto

In alcuni casi, la configurazione della proprietà FillBehavior su Stop sembra non produrre alcun effetto, come quando un'animazione trasferisce il controllo a un'altra perché ha una configurazione HandoffBehavior di SnapshotAndReplace.

Nell'esempio seguente viene creato un Canvas, un Rectangle e un TranslateTransform. Il TranslateTransform verrà animato per spostare il Rectangle intorno al 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>

Gli esempi in questa sezione usano gli oggetti precedenti per illustrare diversi casi in cui la proprietà FillBehavior non si comporta come previsto.

FillBehavior="Stop" e HandoffBehavior con più animazioni

A volte sembra che un'animazione ignori la proprietà FillBehavior quando viene sostituita da una seconda animazione. Si prenda l'esempio seguente, che crea due oggetti Storyboard e li usa per animare lo stesso TranslateTransform illustrato nell'esempio precedente.

Il primo Storyboard, B1, anima la proprietà X del TranslateTransform da 0 a 350, spostando il rettangolo di 350 pixel a destra. Quando l'animazione raggiunge la fine della durata e interrompe la riproduzione, la proprietà X ripristina il valore originale, 0. Di conseguenza, il rettangolo si sposta a destra di 350 pixel e quindi torna alla posizione originale.

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

Il secondo Storyboard, B2, anima anche la proprietà X della stessa TranslateTransform. Poiché è impostata esclusivamente la proprietà To dell'animazione in questa Storyboard, l'animazione utilizza il valore corrente della proprietà animata come valore iniziale.


<!-- 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 si fa clic sul secondo pulsante durante la riproduzione della prima Storyboard, è possibile che si verifichi il comportamento seguente:

  1. Il primo storyboard termina e invia di nuovo il rettangolo alla posizione originale, perché l'animazione ha un FillBehavior di Stop.

  2. Il secondo storyboard entra in azione e anima dalla posizione corrente, che ora è 0, a 500.

Ma non è quello che succede. Al contrario, il rettangolo non torna indietro; continua a spostarsi a destra. Questo perché la seconda animazione usa il valore corrente della prima animazione come valore iniziale e fa partire l'animazione da tale valore fino a 500. Quando la seconda animazione sostituisce la prima perché viene usata la SnapshotAndReplaceHandoffBehavior, la FillBehavior della prima animazione non è rilevante.

"FillBehavior" e l'evento "Completed"

Gli esempi successivi illustrano un altro scenario in cui il StopFillBehavior sembra non avere alcun effetto. Anche in questo caso, l'esempio usa uno Storyboard per animare la proprietà X del TranslateTransform che va da 0 a 350. Tuttavia, questa volta l'esempio viene registrato per l'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>

Il gestore eventi Completed avvia un'operazione Storyboard che trasforma la stessa proprietà dal valore corrente fino a 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

Di seguito è riportato il markup che definisce il secondo Storyboard come risorsa.

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

Quando esegui il Storyboard, puoi aspettarti che la proprietà X del TranslateTransform venga animata da 0 a 350, e poi si ripristina a 0 al termine (perché ha un'impostazione FillBehavior di Stop), e poi animare da 0 a 500. Al contrario, il TranslateTransform si anima da 0 a 350 e poi a 500.

Ciò è dovuto all'ordine in cui WPF genera eventi e perché i valori delle proprietà vengono memorizzati nella cache e non vengono ricalcolati a meno che la proprietà non venga invalidata. L'evento Completed viene elaborato per primo perché è stato attivato dalla cronologia principale (il primo Storyboard). Al momento, la proprietà X restituisce ancora il valore animato perché non è ancora stata invalidata. Il secondo Storyboard usa il valore memorizzato nella cache come valore iniziale e inizia l'animazione.

Prestazioni

Le animazioni continuano a essere eseguite dopo lo spostamento da una pagina

Quando si esce da un Page che contiene animazioni in esecuzione, tali animazioni continueranno a essere riprodotte fino a quando il Page non viene sottoposto a raccolta dei rifiuti. A seconda del sistema di navigazione in uso, una pagina da cui si esce potrebbe rimanere in memoria per un periodo di tempo illimitato, mentre consuma risorse con le relative animazioni. Ciò è più evidente quando una pagina contiene animazioni in esecuzione costante ("ambiente").

Per questo motivo, è consigliabile usare l'evento Unloaded per rimuovere le animazioni quando si esce da una pagina.

Esistono diversi modi per rimuovere un'animazione. Le tecniche seguenti possono essere utilizzate per rimuovere le animazioni che appartengono a un Storyboard.

La tecnica successiva può essere usata indipendentemente dalla modalità di avvio dell'animazione.

  • Per rimuovere le animazioni da una proprietà specifica, usare il metodo BeginAnimation(DependencyProperty, AnimationTimeline). Specifica la proprietà da animare come primo parametro e null come secondo. In questo modo tutti gli orologi di animazione verranno rimossi dalla proprietà .

Per altre informazioni sui diversi modi per animare le proprietà, vedere Property Animation Techniques Overview.

L'uso di Compose HandoffBehavior utilizza le risorse di sistema

Quando si applica un Storyboard, AnimationTimelineo AnimationClock a una proprietà utilizzando l'ComposeHandoffBehavior, tutti gli oggetti Clock precedentemente associati a tale proprietà continuano a utilizzare le risorse di sistema; il sistema di temporizzazione non rimuoverà automaticamente questi orologi.

Per evitare problemi di prestazioni quando si applica un numero elevato di timer usando Compose, è necessario rimuovere i timer di composizione dalla proprietà animata dopo che hanno completato il loro ciclo. Esistono diversi modi per rimuovere un orologio.

Si tratta principalmente di un problema per le animazioni sugli oggetti che hanno una durata prolungata. Quando un oggetto viene sottoposto a raccolta dei rifiuti, anche i relativi orologi verranno disconnessi e raccolti dai rifiuti.

Per ulteriori informazioni sugli oggetti clock, vedere Panoramica del sistema di animazione e temporizzazione.

Vedere anche

  • Panoramica dell'animazione