Для определения анимации в XAML применяется объект EventTrigger или триггер событий. Этот объект имеет свойство Actions, которое определяет ряд действий, возникающих в результате генерации события. Само возникающее действие описывается через элемент BeginStoryboard, который и запускает анимацию.
Непосредственно для определения анимации используется объект Storyboard. Он объявляет объект анимации со всеми ее свойствами и параметрами. Например, проанимируем ширину кнопки:
<Window x:Class="AnimationApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AnimationApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Window.Triggers> <EventTrigger RoutedEvent="Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard TargetProperty="Width" TargetName="helloButton"> <DoubleAnimation From="70" To="150" AutoReverse="True" RepeatBehavior="0:0:10" Duration="0:0:3" Completed="ButtonAnimation_Completed" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Window.Triggers> <Grid> <Button x:Name="helloButton" Width="70" Height="30" Content="Hello" /> </Grid> </Window>
У объекта EventTrigger с помощью атрибута RoutedEvent
определяется событие, которое будет запускать анимацию. В данном случае это событие Loaded - загрузка окна.
Ряд настроек анимации устанавливает элемент Storyboard: TargetName
задает анимируемый элемент, а TargetProperty
определяет свойство элемента,
которое будет анимироваться.
В принципе эти настройки также можно было бы вынести в объект анимации в виде прикрепляемых свойств:
<Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" Storyboard.TargetName="helloButton" From="70" To="150" ...>
И далее в самом объекте Storyboard определяется объект анимации DoubleAnimation с рядом настроек. Все настройки в прицнипе тут те же, что использовались для анимации в прошлой теме. Правда, при определении значений свойств здесь есть некоторые отличия.
Так, свойство RepeatBehavior инициализируется временем - "0:0:10", которое будет повторяться анимация. Чтобы указать число повторов, структуру RepeatBehavior надо инициализировать так: RepeatBehavior="2x", где 2 - количество повторов, а x - просто префикс, указывающий, что речь идет о количестве итераций, без него бы число интерпретировалось как количество дней. Третий способ задания этого свойства - RepeatBehavior="Forever" - в этом случае анимация будет продолжаться все время работы приложения.
Например, ниже установлено RepeatBehavior="Forever", а свойство DecelerationRatio замедляет анимацию, что создает эффект подскока шара в верх, где он, достигая максимальной точки, теряет скорость, а при падении вновь ее увеличивает:
<Window x:Class="AnimationApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AnimationApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Window.Triggers> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard Timeline.DesiredFrameRate="60"> <DoubleAnimation Storyboard.TargetName="ball" Storyboard.TargetProperty="(Canvas.Bottom)" From="0" To="160" AutoReverse="True" Duration="0:0:2.5" RepeatBehavior="Forever" DecelerationRatio="1"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Window.Triggers> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Canvas Background="LightPink"> <Ellipse Name="ball" Fill="Red" Stroke="Black" Width="15" Height="15" Canvas.Left="130" Canvas.Bottom="0" /> </Canvas> <Button Width="70" Height="25" Content="Кнопка" Grid.Row="1" Margin="10" /> </Grid> </Window>
В данном случае так как в EventTrigger в качестве события определено Button.Click
, то анимация будет запускаться по нажатию на кнопку.
По умолчанию анимация вызывается 60 раз в секунду, однако с помощью прикрепленного свойства Timeline.DesiredFrameRate можно задать частоту кадров в секунду, как в предыдущем примере.
Анимация может применяться и к свойствам вложенных объектов, которые являются свойствами, например:
<Ellipse Name="ball" Stroke="Black" Width="20" Height="20" Canvas.Left="130" Canvas.Bottom="0"> <Ellipse.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" GradientOrigin="0.3, 0.3"> <GradientStop Color="White" Offset="0" /> <GradientStop Color="Blue" Offset="1" /> </RadialGradientBrush> </Ellipse.Fill> <Ellipse.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="Fill.GradientStops[1].Color" To="Yellow" Duration="0:0:8" AutoReverse="True" RepeatBehavior="Forever" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Ellipse.Triggers> </Ellipse>
Здесь свойство Fill элемента Ellipse инициализируется кистью RadialGradientBrush, которая имеет коллекцию GradientStops. Анимация же применяется ко второму объекту коллекции и его свойству Color.
С помощью объекта Storyboard можно создавать и более комплексные анимации. Например, сделаем кнопку, которая одновременно меняет ширину, длину и цвет:
<Button x:Name="helloButton" Foreground="White" Width="70" Height="25" Content="Кнопка"> <Button.Background> <SolidColorBrush x:Name="buttonColor" Color="Black" /> </Button.Background> <Button.Triggers> <EventTrigger RoutedEvent="Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" Storyboard.TargetName="helloButton" From="80" To="150" AutoReverse="True" RepeatBehavior="0:0:10" Duration="0:0:2" /> <DoubleAnimation Storyboard.TargetProperty="Height" Storyboard.TargetName="helloButton" From="30" To="100" AutoReverse="True" RepeatBehavior="0:0:10" Duration="0:0:2" /> <ColorAnimation Storyboard.TargetName="buttonColor" Storyboard.TargetProperty="Color" From="{Binding ElementName=buttonColor, Path=Color}" To="Red" AutoReverse="True" RepeatBehavior="0:0:10" Duration="0:0:2" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Button.Triggers> </Button>
При этом необязательно знать имя элемента для анимации, можно прикрепить анимацию ко всем элементам одного типа и установить ее через стиль:
<Window x:Class="AnimationApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AnimationApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Window.Resources> <Style TargetType="Button"> <Style.Triggers> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="Background.Color" To="Red" AutoReverse="True" Duration="0:0:2" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <Button Width="70" Height="25" Content="Кнопка 1" Margin="10" /> <Button Width="70" Height="25" Content="Кнопка 2" Margin="10" /> </StackPanel> </Window>
Так как у стиля задан атриубут TargetType="Button"
, то анимация внутри триггера будет применяться ко всем кнопкам.
В качестве анимируемого свойства указывается "Background.Color". То есть у класса Button есть свойство Background, представленное объектом SolidColorBrush,
а у этого объекта есть свойство Color
. Напрямую анимировать свойство Background мы не можем, так как ColorAnimation требует типа Color.
Так как стиль применяется глобально к кнопкам, то в настройках анимации название анимируемого элемента не указывается. Также не указывается свойство From
,
а это значит, что анимаця будет идти от текущего цвета кнопки.
Теперь после нажатия натажатая кнопка будет ненадолго окрашиваться в красный, а затем возвращать исходный цвет.