Поделиться через


Советы и рекомендации по анимации

Обновлен: Ноябрь 2007

При работе с анимацией в WPF некоторые советы и рекомендации помогут улучшить выполнение анимаций и избавят разработчика от разочарований.

Общие проблемы

Анимация расположения полосы прокрутки или ползунка замораживает их

При анимации расположения полосы прокрутки или ползунка с помощью анимации, имеющей FillBehaviorHoldEnd (значение по умолчанию), пользователь больше не сможет перемещать полосу прокрутки или ползунок. Это происходит потому, что несмотря на завершение анимации она по-прежнему переопределяет базовое значение целевого свойства. Чтобы прекратить переопределение анимацией текущего значения свойства, удалите его или присвойте ему значение FillBehaviorStop. Дополнительные сведения и пример см. в разделе Практическое руководство. Установка свойства после его анимации с помощью раскадровки.

Анимация выходных данных анимации не дает эффекта

Невозможна анимация объекта, являющегося выходным для другой анимации. Например, при использовании ObjectAnimationUsingKeyFrames для анимации FillRectangle из RadialGradientBrush в SolidColorBrush невозможна анимация ни одного из свойств RadialGradientBrush или SolidColorBrush.

Невозможно изменить значение свойства после его анимации

В некоторых случаях нельзя изменить значение свойства после анимации, даже после того, как анимация завершена. Это происходит потому, что несмотря на завершение анимации она по-прежнему переопределяет базовое значение свойства. Чтобы прекратить переопределение анимацией текущего значения свойства, удалите его или присвойте ему значение FillBehaviorStop. Дополнительные сведения и пример см. в разделе Практическое руководство. Установка свойства после его анимации с помощью раскадровки.

Изменение временной шкалы не дает эффекта

Несмотря на то, что большинство свойств Timeline являются анимируемыми и могут быть привязаны к данным, изменение значений свойств активного Timeline не дает никакого эффекта. Это происходит потому, что после начала Timeline система времени создает копию Timeline и использует ее для создания объекта Clock. Изменение исходного объекта не влияет на копию системы.

Для того, чтобы Timeline мог отражать изменения, его часы должны быть повторно созданы и использованы для замены ранее созданных часов. Автоматическое обновление часов не поддерживается. Ниже приведены несколько способов применения изменений шкалы времени:

  • Если шкала времени принадлежит Storyboard, можно заставить ее отражать изменения посредством применения ее раскадровки при помощи метода BeginStoryboard или Begin. Это влечет за собой побочный эффект в виде перезапуска анимации. В коде можно использовать метод Seek для возврата раскадровки обратно в предыдущее положение.

  • Если анимация применяется непосредственно к свойству с помощью метода BeginAnimation, снова вызовите метод BeginAnimation и передайте ему измененную анимацию.

  • Если работа ведется непосредственно на уровне часов, создайте и примените новый набор часов и используйте их для замены предыдущего набора созданных часов.

Дополнительные сведения о шкалах времени и часах см. в разделе Общие сведения об анимации и системе управления временем.

FillBehavior.Stop не работает как ожидалось

Иногда присвоение свойству FillBehavior значения Stop не дает эффекта, например, когда одна анимация «передается» к другой, так как ее параметр HandoffBehavior имеет значение SnapshotAndReplace.

В следующем примере создаются объекты Canvas, Rectangle и TranslateTransform. TranslateTransform будет анимирован для перемещения Rectangle вокруг 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>

Примеры в этом разделе используют определенные выше объекты для демонстрации нескольких случаев, в которых свойство FillBehavior не действует так, как ожидалось.

FillBehavior="Stop" и HandoffBehavior с несколькими анимациями

Иногда кажется, что анимация игнорирует свойство FillBehavior при замене второй анимацией. Рассмотрим следующий пример, в котором создается два объекта Storyboard, которые используются для анимации TranslateTransform, показанного в предыдущем примере.

Первый Storyboard, B1, анимирует свойство X класса TranslateTransform от 0 до 350, которое перемещает прямоугольник на 350 точек вправо. Когда анимация завершается и останавливает воспроизведение, свойство X возвращается исходному значению, равному 0. В результате, прямоугольник перемещается вправо на 350 точек, а затем переходит обратно в исходное положение.

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

Второй Storyboard, B2 также анимирует свойство X того же TranslateTransform. Так как установлено только свойство To анимации в этом Storyboard, анимация использует текущее значение анимируемого свойства в качестве начального значения.

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

При нажатии второй кнопки во время воспроизведения первого Storyboard, можно ожидать следующее поведение:

  1. Первая раскадровка заканчивается и отправляет прямоугольник обратно в исходное положение, поскольку анимация имеет FillBehavior в значении Stop.

  2. Вторая раскадровка вступает в действие и выполняет анимацию от текущего положения, которое сейчас равно 0, до положения 500.

Но это не происходит. Вместо этого, прямоугольник не возвращается обратно, а продолжает перемещаться вправо. Это происходит потому, что вторая анимация использует текущее значение первой анимации в качестве начального значения и производит анимацию от этого значения до 500. Когда вторая анимация заменяет первую из-за использования SnapshotAndReplaceHandoffBehaviorFillBehavior первой анимации не имеет значения.

FillBehavior и завершенное событие

В следующем примере демонстрируется другой сценарий, в котором StopFillBehavior, кажется, не дает эффекта. В примере опять используется раскадровка для анимации свойства XTranslateTransform от 0 до 350. Тем не менее, на этот раз в примере регистрируется событие 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>

Обработчик событий Completed запускает другую Storyboard, которая анимирует то же свойство от его текущего значения до 500.

private void StoryboardC_Completed(object sender, EventArgs e)
{

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

Ниже приведена разметка, определяющая вторую Storyboard в качестве ресурса.

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

При запуске Storyboard ожидается, что свойство XTranslateTransform производит анимацию от 0 до 350, возвращается к значению 0 после завершения (поскольку оно имеет значение FillBehaviorStop), а затем анимируется от 0 до 500. Вместо этого TranslateTransform анимируется от 0 до 350, а затем до 500.

Это происходит из-за последовательности, в которой WPF вызывает события, а также из-за того, что значения свойств кэшируются и не пересчитываются, если свойство является действительным. Событие Completed обрабатывается первым, так как оно было вызвано временной шкалой корня (первая Storyboard). На этот раз свойство X по-прежнему возвращает анимированное значение, так как оно еще не было объявлено недействительным. Вторая Storyboard использует кэшированное значение в качестве начального значения и начинает анимацию.

Производительность

Анимация продолжает выполняться после ухода со страницы

При покидании Page, содержащей выполняющуюся анимацию, эти анимации будут продолжать воспроизведение до сборки мусора Page. В зависимости от используемой системы переходов страница, с которой вы ушли, может оставаться в памяти неограниченное количество времени, тем самым потребляя ресурсы анимацией. Это наиболее заметно, если страница содержит постоянно выполняющиеся анимации («анимации окружения»).

По этой причине целесообразно использовать событие Unloaded для удаления анимации при покидании страницы.

Существуют различные способы удалить анимацию. Следующие методы могут использоваться для удаления анимаций, которые принадлежат Storyboard.

Следующий метод может использоваться независимо от того, как была запущена анимация.

  • Чтобы удалить анимацию из определенного свойства, используйте метод BeginAnimation(DependencyProperty, AnimationTimeline). Укажите анимируемое свойство в качестве первого параметра и null — в качестве второго. Это удалит все часы анимации из свойства.

Дополнительные сведения о различных способах анимации свойств см. в разделе Общие сведения о методах анимации свойств.

Использование составного HandoffBehavior потребляет системные ресурсы

При применении Storyboard, AnimationTimeline или AnimationClock к свойству с помощью Compose HandoffBehavior любые объекты Clock, ранее связанные с этом свойством, продолжают потреблять системные ресурсы. Система времени не удаляет эти часы автоматически.

Во избежание проблем с производительностью при применении большого количества часов с помощью Compose, следует удалить созданные часы из свойства анимации после ее завершения. Существует несколько способов удаления часов.

  • Чтобы удалить все часы из свойства, воспользуйтесь методами ApplyAnimationClock(DependencyProperty, AnimationClock) или BeginAnimation(DependencyProperty, AnimationTimeline) анимированного объекта. Укажите анимируемое свойство в качестве первого параметра и null — в качестве второго. Это удалит все часы анимации из свойства.

  • Для удаления определенных AnimationClock из списка часов используйте свойство Controller объекта AnimationClock для извлечения ClockController, затем вызовите метод Remove объекта ClockController. Обычно это выполняется в обработчике событий Completed для часов. Обратите внимание, что только корневые часы могут управляться через ClockController; свойство Controller дочерних часов вернет null. Кроме того, обратите внимание, что событие Completed не будет вызвано, если эффективная продолжительность часов равна бесконечности. В этом случае пользователю необходимо будет определить, когда вызвать Remove.

В основном эта проблема возникает при анимации объектов, имеющих длинное время существования. Если объект уничтожается сборщиком мусора, его часы также будут отключены и собраны как мусор.

Дополнительные сведения об объектах часов см. в разделе Общие сведения об анимации и системе управления временем.

См. также

Основные понятия

Общие сведения об эффектах анимации