Porady i triki animacyjne

Podczas pracy z animacjami w WPF istnieje wiele wskazówek i wskazówek, które mogą sprawić, że animacje będą działać lepiej i zaoszczędzić frustrację.

Ogólne problemy

Animowanie położenia paska przewijania lub suwaka blokuje ją

Jeśli animujesz położenie paska przewijania lub suwaka przy użyciu animacji z wartością FillBehaviorHoldEnd (wartość domyślna), użytkownik nie będzie już mógł przenieść paska przewijania lub suwaka. Dzieje się tak, ponieważ mimo zakończenia animacji nadal przesłania wartość podstawową właściwości docelowej. Aby zatrzymać animację przed zastąpieniem bieżącej wartości właściwości, usuń ją lub nadaj jej wartość FillBehavior .Stop Aby uzyskać więcej informacji i przykład, zobacz Set a Property After Animating It with a Storyboard (Ustawianie właściwości po animowaniu jej za pomocą scenorysu).

Animowanie danych wyjściowych animacji nie ma efektu

Nie można animować obiektu, który jest wynikiem innej animacji. Jeśli na przykład używasz elementu ObjectAnimationUsingKeyFrames do animowania elementu od do RadialGradientBrush obiektu SolidColorBrush, nie możesz animować FillRectangle żadnych właściwości obiektu RadialGradientBrush lub SolidColorBrush.

Nie można zmienić wartości właściwości po animowaniu jej

W niektórych przypadkach może się wydawać, że nie można zmienić wartości właściwości po jej animowanym, nawet po zakończeniu animacji. Dzieje się tak dlatego, że mimo zakończenia animacji nadal przesłania wartość podstawową właściwości. Aby zatrzymać animację przed zastąpieniem bieżącej wartości właściwości, usuń ją lub nadaj jej wartość FillBehavior .Stop Aby uzyskać więcej informacji i przykład, zobacz Set a Property After Animating It with a Storyboard (Ustawianie właściwości po animowaniu jej za pomocą scenorysu).

Zmiana osi czasu nie ma wpływu

Chociaż większość Timeline właściwości jest animatowalnych i może być powiązana z danymi, zmiana wartości właściwości aktywnego Timeline wydaje się nie mieć żadnego wpływu. Dzieje się tak dlatego, że po Timeline rozpoczęciu system chronometrażu tworzy kopię obiektu Timeline i używa go do utworzenia Clock obiektu. Modyfikowanie oryginału nie ma wpływu na kopię systemu.

Aby element Timeline odzwierciedlał zmiany, jego zegar musi być ponownie wygenerowany i użyty do zastąpienia wcześniej utworzonego zegara. Zegary nie są generowane automatycznie. Poniżej przedstawiono kilka sposobów stosowania zmian osi czasu:

  • Jeśli oś czasu jest lub należy do Storyboardelementu , możesz wprowadzić zmiany, stosując ponownie scenorys przy użyciu BeginStoryboard metody lub Begin . Ma to również efekt uboczny ponownego uruchomienia animacji. W kodzie możesz użyć Seek metody , aby wrócić do poprzedniej pozycji scenorysu.

  • Jeśli animacja została zastosowana bezpośrednio do właściwości przy użyciu BeginAnimation metody , wywołaj BeginAnimation metodę ponownie i przekaż ją do zmodyfikowanej animacji.

  • Jeśli pracujesz bezpośrednio na poziomie zegara, utwórz i zastosuj nowy zestaw zegarów i użyj ich do zastąpienia poprzedniego zestawu wygenerowanych zegarów.

Aby uzyskać więcej informacji na temat osi czasu i zegarów, zobacz Animacja i System chronometrażu — omówienie.

Plik FillBehavior.Stop nie działa zgodnie z oczekiwaniami

Czasami ustawienie FillBehavior właściwości Stop wydaje się nie mieć żadnego efektu, na przykład gdy jedna animacja "hands off" do innej, ponieważ ma HandoffBehavior ustawienie SnapshotAndReplace.

Poniższy przykład tworzy obiekt Canvas, a Rectangle i TranslateTransform. Element TranslateTransform zostanie animowany, aby poruszać Rectangle się po obiekcie 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>

W przykładach w tej sekcji użyto powyższych obiektów, aby zademonstrować kilka przypadków, w których FillBehavior właściwość nie zachowuje się tak, jak można się spodziewać.

FillBehavior="Stop" i HandoffBehavior z wieloma animacjami

Czasami wydaje się, że animacja ignoruje jego FillBehavior właściwość, gdy jest zastępowana przez drugą animację. Skorzystaj z poniższego przykładu, który tworzy dwa Storyboard obiekty i używa ich do animowania tego samego, co TranslateTransform pokazano w poprzednim przykładzie.

Pierwszy Storyboardelement , B1animuje X właściwość TranslateTransform od 0 do 350, która przenosi prostokąt 350 pikseli po prawej stronie. Gdy animacja osiągnie koniec czasu trwania i przestanie być odtwarzana, X właściwość powraca do oryginalnej wartości, 0. W rezultacie prostokąt przechodzi do prawej 350 pikseli, a następnie przechodzi z powrotem do oryginalnej pozycji.

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

Drugi Storyboardelement , B2również animuje X właściwość tego samego TranslateTransformobiektu . Ponieważ tylko To właściwość animacji w tym Storyboard ustawieniu, animacja używa bieżącej wartości właściwości, która animuje jako wartość początkową.


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

Jeśli klikniesz drugi przycisk podczas pierwszego Storyboard odtwarzania, możesz oczekiwać następującego zachowania:

  1. Pierwsza scenorys kończy się i wysyła prostokąt z powrotem do oryginalnej pozycji, ponieważ animacja ma FillBehavior wartość Stop.

  2. Drugi scenorys działa i animuje z bieżącej pozycji, która wynosi teraz od 0 do 500.

Ale to nie jest to, co się dzieje. Zamiast tego prostokąt nie przechodzi wstecz; w dalszym ciągu przechodzi do prawej strony. Wynika to z faktu, że druga animacja używa bieżącej wartości pierwszej animacji jako wartości początkowej i animuje z tej wartości do 500. Gdy druga animacja zastępuje pierwszą, ponieważ SnapshotAndReplaceHandoffBehavior jest używana, FillBehavior pierwsza animacja nie ma znaczenia.

FillBehavior i ukończone zdarzenie

W następnych przykładach pokazano inny scenariusz, w którym StopFillBehavior wydaje się, że nie ma żadnego wpływu. W tym przykładzie użyto scenorysu do animowania X właściwości TranslateTransform od 0 do 350. Jednak tym razem przykład rejestruje się w przypadku Completed zdarzenia.

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

Procedura Completed obsługi zdarzeń uruchamia inną, Storyboard która animuje tę samą właściwość z bieżącej wartości do 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

Poniżej znajduje się znacznik, który definiuje drugi Storyboard jako zasób.

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

Po uruchomieniu elementu można oczekiwaćX, że właściwość obiektu TranslateTransform będzie animować z zakresu od 0 do 350, a następnie powrócić do wartości 0 po zakończeniu Storyboard(ponieważ ma FillBehavior ustawienie Stop), a następnie animować z zakresu od 0 do 500. TranslateTransform Zamiast tego animuje od 0 do 350, a następnie do 500.

Wynika to z kolejności, w której WPF zgłasza zdarzenia i dlatego, że wartości właściwości są buforowane i nie są ponownie obliczane, chyba że właściwość jest unieważniona. Zdarzenie Completed jest przetwarzane najpierw, ponieważ zostało wyzwolone przez oś czasu głównego (pierwszy Storyboard). W tej chwili właściwość nadal zwraca wartość animowaną, X ponieważ nie została jeszcze unieważniona. Storyboard Drugi używa buforowanej wartości jako wartości początkowej i zaczyna animować.

Wydajność

Animacje są nadal uruchamiane po przejściu z dala od strony

Gdy odejdziesz od elementu zawierającego Page uruchomione animacje, te animacje będą nadal odtwarzane do momentu Page odebrania pamięci. W zależności od używanego systemu nawigacji strona, z której się odchodzisz, może pozostawać w pamięci przez nieokreślony czas, przez cały czas zużywając zasoby z animacjami. Jest to najbardziej zauważalne, gdy strona zawiera stale uruchomione animacje ("otoczenia").

Z tego powodu warto użyć Unloaded zdarzenia do usunięcia animacji po opuszczeniu strony.

Istnieją różne sposoby usuwania animacji. Poniższe techniki mogą służyć do usuwania animacji należących do obiektu Storyboard.

Następna technika może być używana niezależnie od sposobu uruchomienia animacji.

  • Aby usunąć animacje z określonej właściwości, użyj BeginAnimation(DependencyProperty, AnimationTimeline) metody . Określ właściwość, która jest animowana jako pierwszy parametr, i null jako drugą. Spowoduje to usunięcie wszystkich zegarów animacji z właściwości .

Aby uzyskać więcej informacji na temat różnych sposobów animowania właściwości, zobacz Omówienie technik animacji właściwości.

Korzystanie z narzędzia Redose HandoffBehavior zużywa zasoby systemowe

Jeśli stosujesz właściwość Storyboard, AnimationTimelinelub AnimationClock do właściwości przy użyciu ComposeHandoffBehavior, wszystkie Clock obiekty wcześniej skojarzone z tą właściwością nadal zużywają zasoby systemowe; system chronometrażu nie usunie tych zegarów automatycznie.

Aby uniknąć problemów z wydajnością podczas stosowania dużej liczby zegarów przy użyciu metody Compose, należy usunąć tworzenie zegarów z animowanej właściwości po zakończeniu. Istnieje kilka sposobów usunięcia zegara.

Jest to przede wszystkim problem z animacjami obiektów, które mają długi okres istnienia. Gdy obiekt zostanie odśmiecony, jego zegary również zostaną odłączone i odśmiecane śmieci.

Aby uzyskać więcej informacji na temat obiektów zegara, zobacz Animacja i System chronometrażu — omówienie.

Zobacz też