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

84

Часто анимированный пользовательский интерфейс требует всего лишь создания и конфигурирования правильных объектов анимации и раскадровки. Однако в других сценариях — особенно, когда несколько анимаций происходят одновременно — возможно, придется уделить больше внимания производительности. Некоторые эффекты определенно могут привести к проблемам, например те, что включают видео, крупные растровые изображения и множественные уровни прозрачности; все это создает серьезную нагрузку на ваш центральный процессор. Небрежная их реализация может привести к заметным замедлениям либо к отбору ресурсов процессора у других приложений, выполняющихся в то же самое время.

К счастью, в WPF предусмотрен ряд трюков, которые могут помочь в таких ситуациях.

Частота кадров

WPF пытается выполнять анимацию с частотой 60 кадров в секунду. Это гарантирует гладкую, плавную анимацию от начала до конца. Конечно, WPF может и не справиться с такой задачей. Если запущено множество сложных анимаций одновременно, и процессор или видеокарта не справляется с нагрузкой, общая частота кадров может снизиться (в лучшем случае) либо анимация начнет отображаться прыжками (в худшем случае).

Редко когда требуется повышать частоту кадров, но может понадобиться ее снизить. Такое решение может быть продиктовано одной из двух причин:

Иногда разработчики предполагают, что WPF включает код, масштабирующий частоту кадров, снижая ее для менее производительной видеоаппаратуры. На самом деле это вовсе не так. Вместо этого WPF всегда пытается достичь 60 кадров в секунду, если только не указано иначе. Чтобы оценить, как выполняется анимация, и удается ли WPF получить частоту 60 кадров в секунду на конкретном компьютере, можно воспользоваться инструментом Perforator, который входит в состав Microsoft Windows SDK v7.0.

Отрегулировать частоту кадров достаточно просто. Для этого просто используется присоединенное свойство Timeline.DesiredFrameRate раскадровки, содержащей анимацию. Ниже приведен пример, сокращающий вдвое частоту кадров:

<Storyboard
  Timeline.DesiredFrameRate="30">

Ниже показано простое тестовое приложение, анимирующее передвигающийся по Canvas кружок. Приложение начинается с помещения объекта Ellipse на Canvas. Свойство Canvas.ClipToBounds устанавливается в true, так что границы кружка не выходят за границу Canvas на остальное поле окна.

Для перемещения кружка по Canvas запускаются одновременно две анимации: одна обновляет свойство Canvas.Left (перемещая его слева направо), а другая изменяет свойство Canvas.Тор (вызывая перемещение по вертикали). Анимация Canvas.Тор обратима — как только кружок достигает наивысшей точки, он начинает падать обратно вниз. Анимация Canvas.Left не обратима, однако она выполняется вдвое медленнее, так что обе анимации перемещают кружок одновременно. Финальный трюк заключается в использовании свойства DecelerationRatio анимации Canvas.Тор. Таким образом, кружок поднимается все медленнее, пока не достигнет вершины, что создает более реалистичный эффект.

Ниже приведена полная разметка примера:

<Window.Resources>
        <BeginStoryboard x:Key="bs">
            <Storyboard Timeline.DesiredFrameRate="{Binding ElementName=txtFrameRate,Path=Text}">
                <DoubleAnimation Storyboard.TargetName="ellipse"
                                 Storyboard.TargetProperty="(Canvas.Left)"
                                 From="0" To="300" Duration="0:0:5"></DoubleAnimation>
                <DoubleAnimation Storyboard.TargetName="ellipse"
                                 Storyboard.TargetProperty="(Canvas.Top)"
                                 From="300" To="0" AutoReverse="True" Duration="0:0:2.5"
                                 DecelerationRatio="1"></DoubleAnimation>
            </Storyboard>
        </BeginStoryboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="Window.Loaded">
            <EventTrigger.Actions>
                <StaticResource ResourceKey="bs"></StaticResource>
            </EventTrigger.Actions>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition Height="auto"></RowDefinition>
                    <RowDefinition Height="auto"></RowDefinition>
                </Grid.RowDefinitions>
                <Border BorderThickness="3" BorderBrush="LightBlue" Margin="5">
                    <Canvas ClipToBounds="True">
                       <Ellipse Name="ellipse" Fill="Orange" Width="20" Height="20"></Ellipse>
                    </Canvas>
                </Border>
                <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center" Margin="5">
                    <Label>Частота кадров: </Label>
                    <TextBox Name="txtFrameRate" MinWidth="60"></TextBox>
                </StackPanel>
                <Button Grid.Row="2" Padding="5" MaxWidth="100">Построить</Button>
            </Grid>
Изменение частоты кадров

Обратите внимание, что свойства Canvas.Left и Canvas.Тор заключены в скобки. Это говорит о том, что они не принадлежат целевому элементу (эллипсу), а являются присоединенными свойствами. Кроме того, анимация определена в коллекции Resources окна. Это позволяет запускать анимацию более чем одним способом. В рассматриваемом примере анимация запускается при щелчке на кнопке Repeat (Повтор) и первой загрузке окна.

Действительное назначение этого примера — испытать различные частоты кадров. Чтобы увидеть эффект от определенной частоты кадров, просто необходимо ввести соответствующее число в текстовом поле и щелкнуть на кнопке Repeat. После этого анимация запускается с новой частотой кадров (которая указывается выражением привязки данных), и тут же можно наблюдать результат. На более низких частотах кадров эллипс не движется гладко, а вместо этого беспорядочно скачет по поверхности Canvas.

Можно также изменить в коде свойство Timeline.DesiredFrame. Например, уровень поддержки видеокарты можно определить, прочитав статическое свойство RenderCapability.Tier.

Пройди тесты
Лучший чат для C# программистов