Анимации по ключевым кадрам и на основе функций для реалистичной анимации

Линейные анимации по ключевым кадрам, анимации по ключевым кадрам со значением KeySpline или функции для реалистичной анимации – это три различных методики реализации приблизительно одного и того же сценария: создания анимации раскадровки, которая является немного более сложной и использует поведение нелинейной анимации из начального состояния в конечное состояние.

Предварительные требования

Обязательно ознакомьтесь с разделом Анимации раскадровки. Данный раздел основан на понятиях анимации, которые объясняются в разделе Анимации раскадровки и не будут описаны здесь. Например, в разделе Анимации раскадровки приводится описание нацеливания на анимации, раскадровок как ресурсов, значений свойства Timeline, например Duration, FillBehavior и т. д.

Анимация по ключевым кадрам

Анимации по ключевым кадрам допускают несколько целевых значений, которые достигаются в определенный момент по временной шкале анимации. Другими словами, каждый ключевой кадр может указывать различное промежуточное значение, последний достигнутый ключевой кадр является конечным значением анимации. Указав несколько значений для анимирования, вы можете создавать более сложные анимации. Кроме того, анимации по ключевым кадрам включают различную логику интерполяции, каждая из которых реализуется как отдельный подкласс KeyFrame в каждом типе анимации. В частности, каждый тип анимации по ключевым кадрам имеет вариант Discrete, Linear, Spline и Easing класса KeyFrame для указания ключевых кадров. Например, чтобы указать анимацию, которая нацеливается на Double и использует ключевые кадры, можно объявить ключевые кадры с помощью DiscreteDoubleKeyFrame, LinearDoubleKeyFrame, SplineDoubleKeyFrame и EasingDoubleKeyFrame. Вы можете использовать один или все эти типы в одной коллекции KeyFrames, чтобы изменять интерполяцию при каждом достижении нового ключевого кадра.

В поведении интерполяции каждый ключевой кадр управляет интерполяцией, пока не достигается время KeyTime. Также в это время достигается значение Value. Если существуют дополнительные ключевые кадры, то это значение становится начальным значением следующего ключевого кадра в последовательности.

В момент начала анимации, если не существует ключевого кадра со значением KeyTime, равным "0:0:0", начальным значением является неанимированное значение свойства. Это похоже на то, как действует анимация From/To/By , если нет объекта From.

Продолжительность анимации по ключевым кадрам неявно равна самому высокому значению KeyTime, заданному в любом из ее ключевых кадров. Если требуется, вы можете задать явное значение Duration, но следите за тем, чтобы оно не было меньше значения KeyTime в собственных ключевых кадрах; в противном случае вы обрежете часть анимации.

Помимо Duration, в анимации по ключевым кадрам можно задать все свойства, основанные на Timeline, как в случае анимации From/To/By, поскольку классы анимации по ключевым кадрам также наследуются от Timeline. Эти особые значения приведены ниже.

  • AutoReverse: по достижении последнего ключевого кадра воспроизведение кадров повторяется в обратном порядке, начиная с конца. Это удваивает кажущуюся продолжительность анимации.
  • BeginTime: задерживает запуск анимации. Отсчет временной шкалы для значений KeyTime в кадрах не начинается, пока не будет достигнуто значение BeginTime, что исключает риск обрезания кадров.
  • FillBehavior: определение дальнейших действий по достижении последнего ключевого кадра. FillBehavior не влияет на какие-либо промежуточные ключевые кадры.
  • RepeatBehavior:
    • Если задано значение Forever, ключевые кадры и их временная шкала повторяются бесконечно.
    • Если задан счетчик итерации, временная шкала повторяется указанное количество раз.
    • Если задано значение Duration, временная шкала повторяется, пока не будет достигнуто это значение времени. Это может привести к усечению анимации во время последовательности ключевых кадров, если значение не является целым множителем значения неявной продолжительности временной шкалы.
  • SpeedRatio (не является широко используемым)

Линейные ключевые кадры

Линейные ключевые кадры приводят к простой линейной интерполяции значения, пока не будет достигнуто значение KeyTime кадра. Это поведение интерполяции больше всего похоже на более простые анимации From/To/By , описанные в разделе Раскадровка анимации .

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

<StackPanel>
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimationUsingKeyFrames
              Storyboard.TargetName="myRectangle"
              Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <LinearDoubleKeyFrame Value="1" KeyTime="0:0:0"/>
                <LinearDoubleKeyFrame Value="1.2" KeyTime="0:0:4"/>
                <LinearDoubleKeyFrame Value="2" KeyTime="0:0:5"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </StackPanel.Resources>
</StackPanel>

Дискретные ключевые кадры

Дискретные ключевые кадры не используют интерполяцию вовсе. По достижении значения KeyTime просто применяется новое значение Value. В зависимости от того, какое свойство пользовательского интерфейса анимируется, это часто приводит к тому, что кажется, будто анимация "прыгает". Убедитесь, что это именно то поведение, которое вам нужно. Вы можете свести к минимуму видимые прыжки, увеличив количество объявляемых ключевых кадров, но если вам требуется плавная анимация, рекомендуется использовать линейные или сплайновые ключевые кадры.

Примечание

Дискретные ключевые кадры — это единственный способ анимировать значение, которое не относится к типам Double, Point и Color, с помощью DiscreteObjectKeyFrame. Более подробное описание процесса приводится далее в этом разделе.

Сплайновые ключевые кадры

Сплайновый ключевой кадр создает изменяемый переход между значениями в соответствии со значением свойства KeySpline. Это свойство указывает первую и вторую контрольные точки кривой Безье, которая описывает ускорение анимации. По сути, KeySpline определяет отношение функции и времени, где график зависимости функции от времени является формой этой кривой Безье. Обычно указывается значение KeySpline в строке собирательного атрибута XAML, содержащей четыре значения Double, разделенные пробелами или запятыми. Эти значения являются парами "X,Y" двух контрольных точек кривой Безье. "X" – это время, а "Y" – модификатор функции в зависимости от значения. Каждое значение всегда должно находиться в диапазоне от 0 до 1 включительно. Без модификации контрольной точки в соответствии со значением KeySpline прямая линия в диапазоне от (0;0) до (1;1) является представлением функции во времени для линейной интерполяции. Контрольные точки изменяют форму этой кривой и, следовательно, поведение функции во времени для сплайновой анимации. Возможно, визуально это лучше рассматривать как график. Вы можете выполнить образец визуализатора ключ-сплайн Silverlight в браузере, чтобы просмотреть, как контрольные точки изменяют кривую и как выполняется анимация примера при использовании его в качестве значения KeySpline.

В следующем примере показано три различных ключевых кадра, применяемых к анимации, которая является ключевой сплайновой анимацией для значения Double (SplineDoubleKeyFrame). Обратите внимание на строку "0.6,0.0 0.9,0.00", применяемую к KeySpline. В результате создается кривая, в которой анимация сначала выполняется медленно, а затем быстро достигает этого значения незадолго до достижения KeyTime.

<Storyboard x:Name="myStoryboard">
    <!-- Animate the TranslateTransform's X property
        from 0 to 350, then 50,
        then 200 over 10 seconds. -->
    <DoubleAnimationUsingKeyFrames
        Storyboard.TargetName="MyAnimatedTranslateTransform"
        Storyboard.TargetProperty="X"
        Duration="0:0:10" EnableDependentAnimation="True">

        <!-- Using a LinearDoubleKeyFrame, the rectangle moves 
            steadily from its starting position to 500 over 
            the first 3 seconds.  -->
        <LinearDoubleKeyFrame Value="500" KeyTime="0:0:3"/>

        <!-- Using a DiscreteDoubleKeyFrame, the rectangle suddenly 
            appears at 400 after the fourth second of the animation. -->
        <DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4"/>

        <!-- Using a SplineDoubleKeyFrame, the rectangle moves 
            back to its starting point. The
            animation starts out slowly at first and then speeds up. 
            This KeyFrame ends after the 6th second. -->
        <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

Ключевые кадры для реалистичной анимации

Ключевой кадр для реалистичной анимации — это ключевой кадр, в котором применяется интерполяция, а управление зависимостью функции от времени интерполяции осуществляется с помощью нескольких предопределенных математических формул. Фактически, используя типы функции для реалистичной анимации, вы можете получить почти такой же результат, что и при использовании сплайнового ключевого кадра, однако существуют некоторые функции для реалистичной анимации, например BackEase, которые невозможно воспроизвести с помощью сплайна.

Чтобы применить функцию для реалистичной анимации к ключевому кадру для реалистичной анимации, установите свойство EasingFunction в качестве элемента свойства в XAML для данного ключевого кадра. В качестве значения укажите элемент объекта для одного из типов функции для реалистичной анимации.

В этом примере CubicEase, а затем BounceEase применяются в качестве последовательных ключевых кадров к DoubleAnimation для создания эффекта отскакивания.

<Storyboard x:Name="myStoryboard">
    <DoubleAnimationUsingKeyFrames Duration="0:0:10"
        Storyboard.TargetProperty="Height"
        Storyboard.TargetName="myEllipse">

        <!-- This keyframe animates the ellipse up to the crest 
            where it slows down and stops. -->
        <EasingDoubleKeyFrame Value="-300" KeyTime="00:00:02">
            <EasingDoubleKeyFrame.EasingFunction>
                <CubicEase/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>

        <!-- This keyframe animates the ellipse back down and makes
            it bounce. -->
        <EasingDoubleKeyFrame Value="0" KeyTime="00:00:06">
            <EasingDoubleKeyFrame.EasingFunction>
                <BounceEase Bounces="5"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

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

Функции для реалистичной анимации

Функции плавности позволяют применять к анимациям настраиваемые математические формулы. Математические операции часто полезны для создания анимаций, которые имитируют реальную физику в двухмерной системе координат. Например, требуется реалистичный отскок объекта или его поведение так, словно он подвешен на пружине. Для приближения этих эффектов можно использовать ключевые кадры или даже анимации From/To/By , но для этого потребуется значительное количество работы, и анимация будет менее точной, чем с помощью математической формулы.

Функции для реалистичной анимации можно применять к анимациям тремя способами.

Ниже представлен список функций для реалистичной анимации.

  • BackEase: объект немного отводится в противоположную сторону, прежде чем начинает двигаться по заданному пути.
  • BounceEase: создается эффект отскакивания.
  • CircleEase: создается анимация, которая ускоряется или замедляется на основании круговой функции.
  • CubicEase: создается анимация, которая ускоряется или замедляется на основании формулы f(t) = t3.
  • ElasticEase: создается анимация, напоминающая колебания пружины взад и вперед до полной остановки.
  • ExponentialEase: создается анимация, которая ускоряется или замедляется на основании экспоненциальной формулы.
  • PowerEase: создается анимация, которая ускоряется или замедляется на основании формулы f(t) = tp, где p равно свойству Power.
  • QuadraticEase: создается анимация, которая ускоряется или замедляется на основании формулы f(t) = t2.
  • QuarticEase: создается анимация, которая ускоряется или замедляется на основании формулы f(t) = t4.
  • QuinticEase: создается анимация, которая ускоряется или замедляется на основании формулы f(t) = t5.
  • SineEase: создается анимация, которая ускоряется или замедляется на основании синусоидальной функции.

Некоторые из функций для реалистичной анимации имеют собственные свойства. Например, BounceEase имеет два свойства Bounces и Bounciness, которые изменяют поведение функции во времени данной конкретной функции BounceEase. Другие функции для реалистичной анимации, такие как CubicEase, не имеют иных свойств, кроме свойства EasingMode, общего для всех функций для реалистичной анимации, и всегда создают одинаковое поведение функции во времени.

Некоторые из этих функций для реалистичной анимации немного перекрываются в зависимости от настроек свойств функций для реалистичной анимации, имеющих их. Например, QuadraticEase абсолютно совпадает с PowerEase со значением Power, равным 2. Кроме того, CircleEase обычно является значением по умолчанию ExponentialEase.

Функция для реалистичной анимации BackEase уникальна, поскольку она может изменять значение за пределами обычного диапазона, заданного с помощью From/To или значений ключевых кадров. Он запускает анимацию, изменяя значение в противоположном направлении, как ожидалось бы от обычного поведения From/To , возвращается к значению с или снова начинается, а затем запускает анимацию в обычном режиме.

В предыдущем примере мы показали, как объявить функцию для реалистичной анимации в случае анимации по ключевым кадрам. В следующем примере функция реалистичной анимации применяется к анимации From/To/By .

<StackPanel x:Name="LayoutRoot" Background="White">
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimation From="30" To="200" Duration="00:00:3" 
                Storyboard.TargetName="myRectangle" 
                Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <DoubleAnimation.EasingFunction>
                    <BounceEase Bounces="2" EasingMode="EaseOut" 
                                Bounciness="2"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
    </StackPanel.Resources>
    <Rectangle x:Name="myRectangle" Fill="Blue" Width="200" Height="30"/>
</StackPanel>

Когда функция реалистичной анимации применяется к анимации From/To/By , она изменяет характеристики функции с течением времени, определяющие интерполяцию значения между значениями From и To в течение длительности анимации. Без функции для реалистичной анимации это была бы линейная интерполяция.

Анимация дискретного значения объекта

Один тип анимации заслуживает особого внимания, поскольку это единственный способ применения анимированного значения к свойствам, не принадлежащим к типу Double, Point или Color. Это анимация по ключевым кадрам ObjectAnimationUsingKeyFrames. Анимирование с помощью значений Object отличается, поскольку возможность интерполяции значений между кадрами отсутствует. По достижении значения KeyTime кадра анимированное значение незамедлительно становится равным значению, указанному в Value ключевого кадра. Поскольку интерполяции нет, существует только один ключевой кадр, который можно использовать в коллекции ключевых кадров ObjectAnimationUsingKeyFrames: DiscreteObjectKeyFrame.

Значение ValueDiscreteObjectKeyFrame часто задается с помощью синтаксиса элемента свойства, поскольку значение объекта, которое вы пытаетесь задать, часто не может быть выражено в качестве строки для заполнения Value в синтаксисе атрибута. Вы по-прежнему можете использовать синтаксис атрибута, если используется образец, например StaticResource.

Одним из случаев, когда можно увидеть ObjectAnimationUsingKeyFrames, используемый в шаблонах по умолчанию, является ситуация, когда свойство шаблона ссылается на ресурс Brush. Эти ресурсы являются объектами SolidColorBrush, а не только значением Color, и используют ресурсы, определенные как системные темы (ThemeDictionaries). Их можно назначить непосредственно значению типа Brush, такому как TextBlock.Foreground; непрямое нацеливание использовать не требуется. Но поскольку SolidColorBrush не является Double, Point или Color, необходимо применить ObjectAnimationUsingKeyFrames для использования ресурса.

<Style x:Key="TextButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid Background="Transparent">
                    <TextBlock x:Name="Text"
                        Text="{TemplateBinding Content}"/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
...
                       </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Также возможно использование ObjectAnimationUsingKeyFrames для анимирования свойств, использующих значение перечисления. Вот другой пример из именованного стиля, построенного по шаблонам среды выполнения Windows по умолчанию. Обратите внимание на то, как задается свойство Visibility, которое принимает константу перечисления Visibility. В этом случае можно задать значение, используя синтаксис атрибута. Вам потребуется только неполное имя константы из перечисления для задания свойства со значением перечисления, например "Collapsed".

<Style x:Key="BackButtonStyle" TargetType="Button">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">
          <Grid x:Name="RootGrid">
            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CommonStates">
              <VisualState x:Name="Normal"/>
...           <VisualState x:Name="Disabled">
                <Storyboard>
                  <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame Value="Collapsed" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
...
          </VisualStateManager.VisualStateGroups>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Можно использовать несколько DiscreteObjectKeyFrame для набора кадров ObjectAnimationUsingKeyFrames. Анимирование значения Image.Source может быть интересным способом создания анимации слайд-шоу, поскольку пример сценария с несколькими значениями объекта может оказаться полезен.