Советы и рекомендации по анимации
При работе с анимацией в WPF, существует ряд советов и приемов, которые анимациям работают лучше и сэкономить разочарований.
Общие проблемы
Анимация расположения полосы прокрутки или ползунка замораживает их
Если вы анимируете положение полосы прокрутки или ползунка с помощью анимации, которая имеет FillBehavior из HoldEnd (значение по умолчанию), пользователь больше не будет иметь возможность перемещения полосы прокрутки или ползунка. Это происходит из-за того, что, хотя анимация завершена, она по-прежнему переопределяет базовое значение целевого свойства. Чтобы остановить анимация не переопределяла текущее значение свойства, удалить его или присвойте ему FillBehavior из Stop. Дополнительные сведения и пример см. в разделе Установка свойства после его анимации с помощью раскадровки.
Анимация выходных данных анимации ничего не делает
Невозможна анимация объекта, являющегося выходным объектом другой анимации. Например, если вы используете ObjectAnimationUsingKeyFrames для анимации Fill из Rectangle из RadialGradientBrush для SolidColorBrush, невозможно анимировать никакие свойства RadialGradientBrush или SolidColorBrush.
Невозможно изменить значение свойства после его анимации
В некоторых случаях может получиться так, что вы не сможете изменить значение свойства после его анимации, даже после завершения анимации. Это происходит из-за того, что, хотя анимация завершена, она по-прежнему переопределяет базовое значение свойства. Чтобы остановить анимация не переопределяла текущее значение свойства, удалить его или присвойте ему FillBehavior из Stop. Дополнительные сведения и пример см. в разделе Установка свойства после его анимации с помощью раскадровки.
Изменение временной шкалы ничего не делает
Несмотря на то что большинство 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 — воспроизведение, можно предположить следующее поведение:
Первая раскадровка заканчивается и отправляет прямоугольник в исходное положение, поскольку анимация имеет FillBehavior из Stop.
Вторая раскадровка вступает в силу и анимируется от текущей позиции, которая теперь имеет значение 0, до 500.
Но это не происходит. Прямоугольник не прыгает обратно; он продолжает перемещаться вправо. Это происходит потому, что вторая анимация использует текущее значение первой анимации в качестве своего начального значения и анимируется от этого значения до 500. Когда вторая анимация заменяет первый, так как SnapshotAndReplaceHandoffBehavior используется, FillBehavior первой анимации не имеет значения.
FillBehavior и завершенное событие
В следующем примере показывается другой сценарий, в котором StopFillBehavior не дает эффекта. Опять же, в примере используется раскадровка для анимации X свойство TranslateTransform от 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, можно ожидать X свойство TranslateTransform анимируется от 0 до 350, затем вернется к 0 после ее завершения (поскольку в нем FillBehavior параметр Stop) и затем будет анимироваться от 0 до 500. Вместо этого TranslateTransform выполняет анимацию от 0 до 350, а затем до 500.
Это порядок, в котором WPF вызывает события и потому, что значения свойств кэшируются и не пересчитываются, если свойство является действительным. Completed Событие обрабатывается первым, так как оно было вызвано корневой временной шкалой (первой Storyboard). В настоящее время X свойство по-прежнему возвращает свое анимированное значение, так как он еще не стало недействительным. Второй Storyboard использует кэшированное значение в качестве своего начального значения и начинает анимацию.
Производительность
Анимация продолжает выполняться после ухода со страницы
При переходе от Page , есть работающие анимации, эти анимации продолжают выполняться до Page удаляется сборщиком мусора. В зависимости от используемой системы навигации страница, с которой ушли, может оставаться в памяти неограниченное количество времени, постоянно потребляя ресурсы на анимацию. Это наиболее заметно, когда страница содержит постоянно выполняющиеся анимации ("фоновые").
По этой причине рекомендуется использовать Unloaded событий для удаления анимаций, когда вы уходите со страницы.
Анимацию можно удалить разными способами. Следующие методы можно использовать для удаления анимаций, принадлежащих Storyboard.
Чтобы удалить Storyboard вы начали с помощью триггера события, см. в разделе как: Удаление раскадровки.
Чтобы использовать код для удаления Storyboard, см. в разделе Remove метод.
Следующий метод может использоваться независимо от того, как была запущена анимация.
- Чтобы удалить анимацию из определенного свойства, используйте BeginAnimation(DependencyProperty, AnimationTimeline) метод. Укажите в качестве первого параметра анимируемое свойство и
null
как второй. Это удалит из свойства все часы анимации.
Дополнительные сведения о различных способах анимации свойств см. в разделе Общие сведения о методах анимации свойств.
Использование составного HandoffBehavior потребляет системные ресурсы
При применении Storyboard, AnimationTimeline, или AnimationClock к свойству с помощью ComposeHandoffBehaviorлюбые Clock ранее связанную с ним объекты по-прежнему потребляют ресурсы системы; система управления временем не удаляет эти часы автоматически.
Чтобы избежать проблем с производительностью при применении большого количества часов через Compose, следует удалять составные часы из анимируемого свойства после их завершения. Есть несколько способов удаления часов.
Чтобы удалить все часы из свойства, используйте ApplyAnimationClock(DependencyProperty, AnimationClock) или BeginAnimation(DependencyProperty, AnimationTimeline) метод анимируемого объекта. Укажите в качестве первого параметра анимируемое свойство и
null
как второй. Это удалит из свойства все часы анимации.Чтобы удалить определенные AnimationClock из списка часов, используйте Controller свойство AnimationClock извлекаемого ClockController, затем вызвать Remove метод ClockController. Обычно это делается Completed обработчик событий для часов. Обратите внимание, что только корневые часы могут управляться ClockController; Controller свойство дочерних часов вернет
null
. Обратите внимание, что Completed событие не будет вызываться, если эффективная продолжительность часов — forever. В этом случае пользователю будет необходимо определить, когда нужно вызывать Remove.
В основном это проблема для анимации объектов, имеющих длинное время жизни. Когда объект собирается как мусор, его часы отсоединяются и также собираются как мусор.
Дополнительные сведения об объектах часов см. в разделе анимации и общие сведения о характере системы.