Анимация вложенных свойств в WinRT

66

Один из простейших вариантов использования трансформаций, о которых будет рассказано позже, обеспечивает перемещение объекта по экрану. Однако трансформации для этого не обязательны - вы можете разместить объект на панели Canvas и анимировать вложенные свойства Canvas.Left и Canvas.Top. Анимация вложенных свойств требует использования специального синтаксиса Storyboard.TargetProperty:

<Page ...>

    <Page.Resources>
        <Storyboard x:Key="storyboard">
            <DoubleAnimation Storyboard.TargetName="ellipse"
                             Storyboard.TargetProperty="(Canvas.Left)"
                             From="0" Duration="0:0:2.5"
                             AutoReverse="True"
                             RepeatBehavior="Forever" />

            <DoubleAnimation Storyboard.TargetName="ellipse"
                             Storyboard.TargetProperty="(Canvas.Top)"
                             From="0" Duration="0:0:1"
                             AutoReverse="True"
                             RepeatBehavior="Forever" />
        </Storyboard>
    </Page.Resources>

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Canvas SizeChanged="Canvas_SizeChanged"
                Margin="0 0 48 48">
            <Ellipse Name="ellipse" Width="48" Height="48" Fill="Red" />
        </Canvas>
    </Grid>

</Page>

Вложенные свойства Canvas.Left и Canvas.Top просто заключаются в круглые скобки. Целью анимации является объект Ellipse, окрашенный в красный цвет. Обратите внимание на отсутствие настройки EnableDependentAnimation. Это означает, что анимации не выполняются в потоке пользовательского интерфейса. Если вы не уверены в том, использовать EnableDependentAnimation или нет, попробуйте не указывать этот параметр. Если анимация работает - все хорошо!

Объект Storyboard содержит двух потомков DoubleAnimation, выполняемых синхронно. Обратите внимание: в каждом из определений DoubleAnimation свойству AutoReverse задается значение True, свойству RepeatBehavior - значение Forever, а свойствам Duration задается продолжительность 1 и 2,5 секунды соответственно. Для обеих анимаций задаются свойства From, но нет свойств To. Это происходит в файле фонового кода:

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;

namespace WinRTTestApp
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            Loaded += (sender, args) =>
            {
                (this.Resources["storyboard"] as Storyboard).Begin();
            };
        }

        private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Storyboard storyboard = this.Resources["storyboard"] as Storyboard;

            // Анимация свойства Canvas.Left
            DoubleAnimation anima = storyboard.Children[0] as DoubleAnimation;
            anima.To = e.NewSize.Width;

            // Анимация свойства Canvas.Top
            anima = storyboard.Children[1] as DoubleAnimation;
            anima.To = e.NewSize.Height;
        }
    }
}

Выполнение Storyboard начинается в обработчике события Loaded. При каждом изменении размера Canvas (которое происходит при изменении размера окна) новые значения To вычисляются по ширине и высоте объекта Canvas, которому в файле XAML назначаются поля (Margin) для компенсации размера Ellipse. Казалось бы, изменение значений текущей анимации должно быть запрещено, но оно нормально работает. В результате получается шарик, который перемещается, отскакивая от краев экрана.

Пример использования анимации вложенных свойств

Оба определения DoubleAnimation включают одинаковые настройки AutoReverse и RepeatBehavior. Как упоминалось ранее, эти свойства определяются классом Timeline, который также является родительским классом для Storyboard. Нельзя ли перенести эти две настройки в тег Storyboard? Давайте попробуем:

<Storyboard x:Key="storyboard"
            AutoReverse="True"
            RepeatBehavior="Forever">
         <DoubleAnimation Storyboard.TargetName="ellipse"
                          Storyboard.TargetProperty="(Canvas.Left)"
                          From="0" Duration="0:0:2.5" />

         <DoubleAnimation Storyboard.TargetName="ellipse"
                          Storyboard.TargetProperty="(Canvas.Top)"
                          From="0" Duration="0:0:1" />
</Storyboard>

Решение выглядит вполне разумно, но работает не так, как предыдущая разметка. Свойство Duration объекта Storyboard определяет продолжительность самой длинной дочерней анимации Storyboard, которая в данном случае равна 2,5 секунды. Анимация начинается с перемещения шарика по горизонтали и вертикали. Но по истечении 1 секунды шарик сталкивается с краем - нижним в альбомной ориентации. Анимация свойства Canvas.Top завершена, но анимация Canvas.Left продолжает перемещать шарик по горизонтали еще 1,5 секунды. В этой стадии шарик находится в правом нижнем углу экрана.

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

Итак, свойства AutoReverse и RepeatBehavior можно переместить в Storyboard только в том случае, если все анимации в Storyboard имеют одинаковую длину.

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