Всем добрый день! В этой статье я опишу простой способ запуска анимации с помощью инструмента Blend SDK от Microsoft.
С анимациями в WPF дела обстоят не очень легко и их стараются избежать по нескольким причинам. Первая — их тяжело запускать и сложно останавливать. Вторая — они не очень быстрые.
Разберемся с запуском — что же такого «сложного». Нарисуем простой ItemsControl, внутри которого есть Canvas и размещаются маленькие шарики.
Демонстрация результата:
Теперь надо сделать их анимириванными, для этого перенесем наши вычисления позиции из Canvas.Left и Canvas.Top в анимацию, которая будет задавать Canvas.Left и Canvas.Top элемента. Разместим её в ресурсах каждого Ellipse'а.
Но теперь вопрос — когда запускать анимации? Однозначно нам нужно запускать при появлении элемента, то есть добавим:
Но что теперь? По хорошему можно забиндить запуск анимации на самих себя же, добавив в Binding каждого вычисления To по NotifyOnTargetUpdated=True. Тогда можно написать
И всё работает. Но не всё так просто. В живом проекте такой подход работать отказался — скорее всего из-за зацикливания вызовов. Также где угодно можно написать NotifyOnTargetUpdated=True и анимации будут вызываться лишний раз. Нам же нужно, чтобы анимации запускались только по изменении свойств X и Y. Тут то и приходит нам на выручку Blend SDK с его библиотеками Microsoft.Expression.Interactions и System.Windows.Interactivity.
Добавляем их в проект (я добавлял через nuget). И прописываем нужные нам xmlns:
Теперь можно внутри каждого Ellipse'а написать:
И анимации запустились:
Jobs done!
Мы не запускаем их лишний раз и они работают так как нам нужно. К тому же Blend SDK содержит множество других простых и, при этом, невероятно полезных вещей — советую ознакомиться:
Microsoft.Expression.Interactions documentation
System.Windows.Interactivity documentation
И, возвращаясь к началу статьи, хочу обратить внимание на второй пункт — то, что анимации очень медленные. Да — они такие, что с этим делать я не знаю, но тем не менее буду стараться выяснить.
Всем спасибо за внимание!
> Ссылка на проект на github
С анимациями в WPF дела обстоят не очень легко и их стараются избежать по нескольким причинам. Первая — их тяжело запускать и сложно останавливать. Вторая — они не очень быстрые.
Разберемся с запуском — что же такого «сложного». Нарисуем простой ItemsControl, внутри которого есть Canvas и размещаются маленькие шарики.
<ItemsControl ItemsSource="{Binding Bubbles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse
Width="10"
Height="10"
Fill="#FF616AB6" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Демонстрация результата:
Теперь надо сделать их анимириванными, для этого перенесем наши вычисления позиции из Canvas.Left и Canvas.Top в анимацию, которая будет задавать Canvas.Left и Canvas.Top элемента. Разместим её в ресурсах каждого Ellipse'а.
<Ellipse.Resources>
<Storyboard x:Key="yanimation">
<DoubleAnimation
Storyboard.Target="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}"
Storyboard.TargetProperty="(Canvas.Top)"
Duration="0:0:1" To="{Binding Y}" />
</Storyboard>
<Storyboard x:Key="xanimation">
<DoubleAnimation
Storyboard.Target="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:1" To="{Binding X}" />
</Storyboard>
</Ellipse.Resources>
Но теперь вопрос — когда запускать анимации? Однозначно нам нужно запускать при появлении элемента, то есть добавим:
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard Storyboard="{StaticResource xanimation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard Storyboard="{StaticResource yanimation}" />
</EventTrigger>
</Ellipse.Triggers>
Но что теперь? По хорошему можно забиндить запуск анимации на самих себя же, добавив в Binding каждого вычисления To по NotifyOnTargetUpdated=True. Тогда можно написать
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource xanimation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource yanimation}" />
</EventTrigger>
</Ellipse.Triggers>
И всё работает. Но не всё так просто. В живом проекте такой подход работать отказался — скорее всего из-за зацикливания вызовов. Также где угодно можно написать NotifyOnTargetUpdated=True и анимации будут вызываться лишний раз. Нам же нужно, чтобы анимации запускались только по изменении свойств X и Y. Тут то и приходит нам на выручку Blend SDK с его библиотеками Microsoft.Expression.Interactions и System.Windows.Interactivity.
Добавляем их в проект (я добавлял через nuget). И прописываем нужные нам xmlns:
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Теперь можно внутри каждого Ellipse'а написать:
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding X}">
<ei:ControlStoryboardAction Storyboard="{StaticResource xanimation}" />
</ei:PropertyChangedTrigger>
<ei:PropertyChangedTrigger Binding="{Binding Y}">
<ei:ControlStoryboardAction Storyboard="{StaticResource yanimation}" />
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
И анимации запустились:
Jobs done!
Мы не запускаем их лишний раз и они работают так как нам нужно. К тому же Blend SDK содержит множество других простых и, при этом, невероятно полезных вещей — советую ознакомиться:
Microsoft.Expression.Interactions documentation
System.Windows.Interactivity documentation
И, возвращаясь к началу статьи, хочу обратить внимание на второй пункт — то, что анимации очень медленные. Да — они такие, что с этим делать я не знаю, но тем не менее буду стараться выяснить.
Всем спасибо за внимание!
> Ссылка на проект на github
virtyaluk
Всегда удивляло насколько гибкая и продвинутая анимация в WPF. И насколько прожорлива к ресурсам. В свое время хотел запилить ripple-эффект а ля Material Design, но анимация нещадно жрала CPU и RAM. На этом все затухло. Умеют в Microsoft сделать крутую фичу, но не умеют довести ее до ума :(
Kiel Автор
Так хочется чуть чуть верить в то, что Microsoft доведет UWP до ума ))
IL_Agent
Эх, лучше wpf доводили… А так, боюсь, что uwp постигнет забвение, как и все остальные разработки, с выходом ккой-нибудь очередной версии винды (
Kiel Автор
И я совсем скачусь в циника ) Всё жду когда у них возникнет жгучее желание объединить все свои наработки и сделать единую WPF для linux / windows / mobile.
Мечты.
Dolbe
Xamarin же. Но он не умеет в Linux, вроде.
Kiel Автор
Сейчас ничего не умеет под Linux. Пытались реализовать Moonlight и забросили в итоге. Это еще будущее. И тем не менее — даже в Xamarin / UWP / WPF присутствует фрагментированность.
Смотрим кто победит ;) Но пока всё намекает на Html и JS приложения )
expeon
dotnet.github.io
github.com/Microsoft/xaml-standard
Все в наших с Вами руках :)
Kiel Автор
Тут только мои рук не хватит ))) Хоть стартап открывай!