• Nie Znaleziono Wyników

Budowa aplikacji w technologii .NET wykład 11 – Animacje

N/A
N/A
Protected

Academic year: 2021

Share "Budowa aplikacji w technologii .NET wykład 11 – Animacje"

Copied!
52
0
0

Pełen tekst

(1)

Budowa aplikacji w technologii .NET

wykład 11 – Animacje

Wykonanie animacji w Windows Forms, MFS, Swingu, etc. opierało się na timerach i własnej funkcji rysującej:

1. Stworzenie timera, który co określoną liczbę milisekund odpala zdarzenie.

2. W reakcji na zdarzenie timera, należy dokonać wymaganych przez animację obliczeń, zmian parametrów, etc. (np. jeśli animujemy obracające się logo – zwiększamy kąt obrotu o stały stopień).

3. Następnie unieważniamy zawartość okna. Zmusi to system do wywołania naszej procedury rysującej.

4. W tej procedurze rysujemy kolejną klatkę animacji.

(2)

Wiążą się z tym problemy:

• Procedura rysująca odpowiada za wyświetlanie pikseli, nie kontrolek – zatem zawartość animacji musi być rysowana ręcznie (w WPF animacji podlegają dowolne własności elementów).

• Dodanie kolejnej animacji komplikuje kod (w WPF można składać bardziej złożone animacji z kilku prostszych).

• Stały framerate (ustawiony w timerze). Jego modyfikacja pociąga za sobą zmianę procedury obliczającej kolejną klatkę.

• Bardziej złożone sposoby animacji (np. przemieszczanie po ścieżce) wymagają bardziej złożonego kodu (w WPF animacje definiuje się w XAMLu).

W WPF model jest prostszy, gdyż okna samo dba o aktualizację swej zawartości, gdy zmienią się własności elementów. Ponadto nie wymaga ręcznej zmiany własności w reakcji na zdarzenia timera. Zamiast tego animacje definiuje się w sposób deklaratywny.

Animacja własności

Animacja w WPF nie wymaga zajmowania się serią klatek. Zamiast tego określamy jaka własność elementu w jaki sposób ma się zmieniać w czasie.

Ograniczenie: animacje dotyczą tylko dependency properties (a co za tym idzie, nie da się uzyskać w ten sposób efektów, których nie osiągniemy modyfikując własności, np. nie można animacją dodawać elementów do okna). Mogą one służyć uatrakcyjnieniu standardowej aplikacji, ale nie nadają się np. do gier.

(3)

Podstawy

Każda animacja operuje na pojedynczej własności.

Animowanie własności wymaga użycia klasy animacji właściwej dla typu tej własności.

Klasy animacji:

zmieniających pewną wartość na drodze liniowej interpolacji (z wartości From do wartości To lub zwiększając wartość aktualną o By):

◦ ByteAnimation

◦ ColorAnimation

◦ DecimalAnimation

◦ DoubleAnimation

◦ Int16Animation

◦ Int32Animation

◦ Int64Animation

◦ PointAnimation

◦ Point3DAnimation

◦ QuarternionAnimation

◦ RectAnimation

◦ Rotation3DAnimation

◦ SingleAnimation

◦ SizeAnimation

◦ ThicknessAnimation

◦ VectorAnimation

◦ Vector3DAnimation

(4)

• zmieniających pewną wartość w określonych punktach czasu:

BooleanAnimationUsingKeyFrames

ByteAnimationUsingKeyFrames

CharAnimationUsingKeyFrames

ColorAnimationUsingKeyFrames

DecimalAnimationUsingKeyFrames

DoubleAnimationUsingKeyFrames

Int16AnimationUsingKeyFrames

Int32AnimationUsingKeyFrames

Int64AnimationUsingKeyFrames

MatrixAnimationUsingKeyFrames

ObjectAnimationUsingKeyFrames

PointAnimationUsingKeyFrames

Point3DAnimationUsingKeyFrames

QuarternionAnimationUsingKeyFrames

RectAnimationUsingKeyFrames

Rotation3DAnimationUsingKeyFrames

SingleAnimationUsingKeyFrames

SizeAnimationUsingKeyFrames

StringAnimationUsingKeyFrames

ThicknessAnimationUsingKeyFrames

VectorAnimationUsingKeyFrames

Vector3DAnimationUsingKeyFrames

(5)

• animacje wykorzystujące ścieżkę (PathGeometry):

◦ DoubleAnimationUsingPath

◦ MatrixAnimationUsingPath

◦ PointAnimationUsingPath

Możliwe jest też tworzenie własnych klas animacji.

Nie ma klas do animowania własności typu wyliczeniowego (np. HorizontalAlignment).

Zazwyczaj nie animuje się też typów referencyjnych (np. Brush – zamiast tego używa się np. ColorAnimation, DoubleAnimation do animacji własności pędzla).

Animacje w kodzie

Wymaga stworzenia obiektu animacji, ustawienia własności i odpalenia jej przy pomocy metody BeginAnimation() (zadeklarowanej w interfejsie IAnimatable).

<Grid>

<Button Name="cmdPowiększ" HorizontalAlignment="Center"

VerticalAlignment="Center" Padding="3"

Click="PowiększClick" >Powiększ</Button>

</Grid>

(6)

private void PowiększClick(object sender, RoutedEventArgs e) {

DoubleAnimation anim = new DoubleAnimation();

anim.From = cmdPowiększ.ActualWidth;

anim.To = this.Width - 30;

anim.Duration = TimeSpan.FromSeconds(3);

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

}

(7)

From – początkowa wartość własności (gdybyśmy ustawili tu stałą wartość, wówczas własność zostałaby zresetowana do niej na początku animacji):

anim.From = 160;

Jeśli jej nie określimy, animacja zaczyna się od wartości aktualnej (ale musiała być ona wcześniej jawnie ustawiona: w kodzie, w XAMLu lub przez settera, ale nie przez kontener).

private void Window_Loaded(object sender, RoutedEventArgs e) {

cmdPowiększ.Width = cmdPowiększ.ActualWidth;

}

To – wartość końcowa. Podobnie jak From, można ją opuścić. Oznaczać to będzie powrót do początkowej wartości (czyli aktualnej wartości, nie licząc animacji).

DoubleAnimation anim = new DoubleAnimation();

anim.Duration = TimeSpan.FromSeconds(3);

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

(8)

Aby to zadziałało, aktualna wartość szerokości już musiała zostać zmodyfikowana przez jakąś animację. Możemy to wykorzystać, aby stworzyć animację powrotu (wykorzystując zdarzenie Completed, oznaczające zakończenie animacji):

private void PowiększClick(object sender, RoutedEventArgs e) {

DoubleAnimation anim = new DoubleAnimation();

anim.To = this.Width - 30;

anim.Duration = TimeSpan.FromSeconds(3);

anim.Completed += anim_Completed;

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

}

void anim_Completed(object sender, EventArgs e) {

DoubleAnimation anim = new DoubleAnimation();

anim.Duration = TimeSpan.FromSeconds(3);

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

}

(9)

Inne rozwiązanie (automatyczny powrót do pierwotnej własności – odwrócenie animacji):

anim.AutoReverse = true;

Inne rozwiązanie (powrót do pierwotnej własności bez animacji):

anim.FillBehavior = FillBehavior.Stop;

Animacja po zakończeniu nadal istnieje i ma wpływ na aktualną wartość własności. Aby zlikwidować wpływ animacji, należy usunąć jej obiekt:

cmdPowiększ.BeginAnimation(Button.WidthProperty, null);

By – można użyć, zamiast podawania To. Zmienia własność o podaną wartość.

anim.By = 100;

odpowiada to:

anim.To = cmdPowiększ.Width + 100;

(10)

Podobny efekt można osiągnąć ustawiając flagę IsAdditive:

DoubleAnimation anim = new DoubleAnimation();

anim.From = 0;

anim.To = 100;

anim.Duration = TimeSpan.FromSeconds(3);

anim.IsAdditive = true;

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

Duration – określamy czas trwania a nie szybkość animacji – zatem powiększanie przycisku zawsze będzie trwało 3 sekundy, niezależnie od rozmiaru okna.

Uwaga: na własności nie można wykonywać kilku animacja na raz (odpalenie kolejnej, przerywa poprzednią). Może spowodować to wrażenie, że animacja zwolniła – gdyż dla nowej animacji znów ustawiono czas trwania 3 sekundy.

Można odpalić (niezależnie) animacje różnych własności elementu:

cmdPowiększ.BeginAnimation(Button.WidthProperty, ...);

cmdPowiększ.BeginAnimation(Button.HeightProperty, ...);

(11)

Inne własności animacji:

BeginTime – wymusza opóźnienie rozpoczęcia animacji (TimeSpan).

SpeedRatio – szybkość animacji, domyślnie 1.

AccelerationRatio, DecelerationRatio – do animacji nieliniowej, która przyśpiesza na początku, a zwalnia na końcu. Wartości od 0 do 1 określają jaką część czasu trwania ma zająć przyśpieszanie lub zwalnianie.

DoubleAnimation anim = new DoubleAnimation();

anim.To = this.Width - 30;

anim.Duration = TimeSpan.FromSeconds(5);

anim.AccelerationRatio = 0.3;

anim.DecelerationRatio = 0.3;

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

(12)

RepeatBehavior – pozwala powtarzać animację określoną liczbę razy, przez określony czas lub bez końca (RepeatBehavior.Forever).

DoubleAnimation anim = new DoubleAnimation();

anim.To = this.Width - 30;

anim.Duration = TimeSpan.FromSeconds(0.5);

anim.AutoReverse = true;

anim.RepeatBehavior = new RepeatBehavior(3);

cmdPowiększ.BeginAnimation(Button.WidthProperty, anim);

lub:

anim.RepeatBehavior =

new RepeatBehavior(TimeSpan.FromSeconds(2.5));

Można ustawić flagę IsCumulative, wówczas kolejne powtórzenie animacji rozpocznie się od ostatniego stanu poprzedniej (a nie stanu początkowego – działa to jak domyślne ustawienie IsAdditive dla kolejnych powtórzeń).

(13)

Animacje w XAMLu

Animacja w XAMLu definiowana jest w obiekcie storyboard. Reprezentuje on przebieg animacji, pozwala na grupowanie kilku animacji, kontrolowanie animacji. Ponadto pełni podobną rolę, co wywołanie metody BeginAnimation().

<Storyboard TargetName="cmdPowiększ" TargetProperty="Width">

<DoubleAnimation From="160" To="300" Duration="0:0:5" />

</Storyboard>

Inny wariant (TargetName i TargetProperty to własności dołączone):

<Storyboard>

<DoubleAnimation Storyboard.TargetName="cmdPowiększ"

Storyboard.TargetProperty="Width"

From="160" To="300" Duration="0:0:5" />

</Storyboard>

(14)

Do uruchomienia animacji konieczny jest EventTrigger. Jego zadaniem jest odpalenie animacji. (EventTrigger nie musi znajdować sie w Stylu.)

<Button Padding="3" HorizontalAlignment="Center"

VerticalAlignment="Center" Width="160">

<Button.Triggers>

<EventTrigger RoutedEvent="Button.Click">

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<DoubleAnimation To="300"

Duration="0:0:5"

Storyboard.TargetProperty="Width" />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

</Button.Triggers>

Powiększ

</Button>

Nie musimy określać TargetName – wówczas automatycznie użyty zostanie element, w którym zagnieżdżono storyboard.

(15)

W XAMLu nie ma obsługi wyrażeń, dlatego wartość docelowa została ustawiona na stałą.

Wartość tę można odczytać z innej własności przy pomocy bindingu:

... To="{Binding ElementName=window,Path=Width}" ...

Jeśli chcemy bardziej złożonych obliczeń, powinniśmy napisać własnego

IValueConvertera lub stworzyć własność zależnościową do której będziemy mogli dowiązać i która zajmie się obliczeniami.

Poza EventTriggerem możemy również używać PropertyTriggerów:

<Style x:Key="powiększalski"><Style.Triggers>

<Trigger Property="Button.IsPressed" Value="True">

<Trigger.EnterActions>

<BeginStoryboard>

<Storyboard>

<DoubleAnimation .../>

</Storyboard>

</BeginStoryboard>

</Trigger.EnterActions>

</Trigger>

</Style.Triggers></Style>

(16)

...

<Button ... Style="{StaticResource powiększalski}">

Powiększ

</Button>

Trigger.EnterActions to akcje wykonywane, w chwili gdy własność przyjmuje wskazaną wartość. Trigger.ExitActions są wykonywane, gdy następuje utrata tej wartości (wracamy do false).

Wszystkie animacje umieszczone w storyboard są automatycznie synchronizowane:

<Storyboard>

<DoubleAnimation Storyboard.TargetProperty="Width"

By="100" Duration="0:0:0.5" />

<DoubleAnimation Storyboard.TargetProperty="Height"

By="50" Duration="0:0:0.5" />

</Storyboard>

Możemy korzystać ze SpeedRatio, aby przyśpieszyć niektóre animacje, BeginTime, aby opóźnić rozpoczęcie innych, etc.

(17)

Przykład – wykorzystanie do animacji przejścia (między dwoma obrazkami). Obrazki umieszczamy w Gridzie – jeden na drugim:

<Grid>

<Image Source="Gra o tron.jpg" />

<Image Source="Zew Cthulhu.jpg" Name="second" />

</Grid>

W momencie ładowania okienka uruchamiamy animację zmieniającą przeźroczystość górnego obrazka:

<Window.Triggers>

<EventTrigger RoutedEvent="Window.Loaded">

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard Storyboard.TargetName="second">

<DoubleAnimation Duration="0:0:5"

Storyboard.TargetProperty="Opacity"

To="0" />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

</Window.Triggers>

(18)
(19)

Ciekawszym rozwiązaniem jest wykorzystanie i animowanie maski przeźroczystości.

<Image Source="Zew Cthulhu.jpg" Name="second">

<Image.OpacityMask>

<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">

<GradientStop Offset="0" Color="Transparent"

x:Name="transparentStop" />

<GradientStop Offset="0" Color="Black"

x:Name="visibleStop" />

</LinearGradientBrush>

</Image.OpacityMask>

</Image>

...

<Storyboard>

<DoubleAnimation Storyboard.TargetName="visibleStop"

Storyboard.TargetProperty="Offset"

From="0" To="1.2" Duration="0:0:6" />

<DoubleAnimation Storyboard.TargetName="transparentStop"

Storyboard.TargetProperty="Offset" BeginTime="0:0:1"

From="0" To="1" Duration="0:0:5" />

</Storyboard>

(20)
(21)

WPF daje możliwość kontrolowania animacji przy pomocy następujących klas akcji:

• PauseStoryboard

• ResumeStoryboard

• StopStoryboard (zatrzymanie nie jest równoważne zakończeniu, bo nie zachowuje końcowej wartości własności, a wraca do początkowej)

• SeekStoryboard (skok do wskazanej pozycji)

• SetStoryboardSpeedRatio

• SkipStoryboardToFill (skok do końca)

• RemoveStoryboard (zatrzymuje i usuwa) Dodajmy przyciski do kontroli animacji:

<DockPanel>

<StackPanel Orientation="Horizontal"

HorizontalAlignment="Center" DockPanel.Dock="Bottom">

<Button Name="cmdStart" >Start</Button>

<Button Name="cmdPause">Pause</Button>

<Button Name="cmdResume">Resume</Button>

<Button Name="cmdStop">Stop</Button>

</StackPanel>

<Grid Margin="3"> <Image .../> <Image .../> </Grid>

</DockPanel>

(22)

Następnie event trigger do uruchomienia animacji:

<Window.Triggers>

<EventTrigger SourceName="cmdStart"

RoutedEvent="Button.Click">

<BeginStoryboard Name="fadeStoryboardBegin">

<Storyboard>

...

</Storyboard>

</BeginStoryboard>

</EventTrigger>

...

</Window.Triggers>

(23)

W podobny sposób event triggery do pozostałych akcji (podajemy tylko jakiego BeginStoryboard mają dotyczyć). Uwaga: jest to nazwa BeginStoryboard a nie Storyboard.

<Window.Triggers>

...

<EventTrigger SourceName="cmdPause"

RoutedEvent="Button.Click">

<PauseStoryboard

BeginStoryboardName="fadeStoryboardBegin"/>

</EventTrigger>

<EventTrigger SourceName="cmdResume"

RoutedEvent="Button.Click">

<ResumeStoryboard

BeginStoryboardName="fadeStoryboardBegin"/>

</EventTrigger>

<EventTrigger SourceName="cmdStop"

RoutedEvent="Button.Click">

<StopStoryboard

BeginStoryboardName="fadeStoryboardBegin"/>

</EventTrigger>

</Window.Triggers>

(24)

(Możemy to też kontrolować zwyczajnie wywołując metody Stop(), Pause(), Resume(), etc.

z obiektu Storyboard. Ale użycie powyższych akcji pozwala nam łatwo uruchamiać i zatrzymywać animację w triggerach, np. po najechaniu myszą na element.)

(25)

Przebieg animacji możemy monitorować przy użyciu poniższych zdarzeń:

• Completed – animacja została zakończona

• CurrentGlobalSpeedInvalidated – zmianie uległa szybkość animacji (oznacza to również pauzę, wznowienie, zmianę pozycji)

• CurrentStateInvalidated – uruchomienie lub zakończenie

• CurrentTimeInvalidated – tyknięcie zegara animacji (również uruchomienie lub zakończenie)

• RemoveRequested – usuwanie animacji

Może wykorzystać CurrentTimeInvalidated, by dodać wskaźnik zaawansowania animacji.

<DockPanel>

...

<ProgressBar Name="progres" DockPanel.Dock="Bottom"

Height="10" Minimum="0" Maximum="1" />

...

</DockPanel>

(26)

Dodajemy obsługę zdarzenia:

<Window.Triggers>

<EventTrigger ...>

<BeginStoryboard ...>

<Storyboard CurrentTimeInvalidated=

"Storyboard_CurrentTimeInvalidated">

...

</Storyboard>

</BeginStoryboard>

</Window.Triggers>

Nadawcą zdarzenia jest Clock, z którego możemy odczytać postęp animacji:

private void Storyboard_CurrentTimeInvalidated(object sender, EventArgs e) {

Clock clock = (Clock)sender;

if(clock.CurrentProgress != null)

progres.Value = (double)clock.CurrentProgress;

}

(27)
(28)

Domyślnie animacje działają w 60 klatkach na sekundę. Jeśli animacja jest zbyt

wymagająca (intensywnie wykorzystuje przeźroczystość, materiał wideo, duże bitmapy) możemy chcieć ją zmniejszyć, by poprawić wydajność:

<Storyboard Timeline.DesiredFrameRate="30">

...

</Storyboard>

(29)

Posługiwanie się animacjami Co animować?

• Jeśli chcemy, by elementy pojawiały się i znikały: Opacity.

• Jeśli chcemy przemieszczać elementy: Canvas.Left, Canvas.Top (jeśli kontenerem jest Canvas), ewentualnie Margin lub/i Padding (przy pomocy

ThicknessAnimation), ewentualnie MinWidth i MinHeight kolumny lub wiersza w Gridzie.

• RenderTransform – aby przemieszczać (TranslateTransform), obracać

(RotateTransform), skalować (ScaleTransform) elementy. Działają szybciej niż animacja własności określających rozmiar czy pozycję.

• ColorAnimation – aby modyfikować własności pędzla.

(30)

Animacja transformacji

Każdy element ma dwa rodzaje transformacji: RenderTransform (stosowana przed

wyświetleniem obiektu) i LayoutTransform (stosowana przed ułożeniem elementów przez kontener). (Animacja nie może stworzyć transformacji, a jedynie animować własności istniejącej.)

Przykład: obracający się przycisk.

<Button RenderTransformOrigin="0.5,0.5">

<Button.RenderTransform>

<RotateTransform/>

</Button.RenderTransform>

OK

</Button>

(31)

EventTrigger do uruchomienia animacji:

<EventTrigger RoutedEvent="Button.MouseEnter">

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<DoubleAnimation Storyboard.TargetProperty=

"RenderTransform.Angle" To="360" Duration=

"0:0:1" RepeatBehavior="Forever"/>

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

(32)

I do zakończenia (powrotu do stanu początkowego):

<EventTrigger RoutedEvent="Button.MouseLeave">

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<DoubleAnimation Storyboard.TargetProperty=

"RenderTransform.Angle" Duration="0:0:0.2"/>

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

(33)

Parametry przycisku możemy ustawić w stylu:

<Style TargetType="Button">

<Setter Property="Margin" Value="3" />

<Setter Property="Padding" Value="20,3"/>

<Setter Property="HorizontalAlignment" Value="Center"/>

<Setter Property="VerticalAlignment" Value="Center"/>

<Setter Property="RenderTransformOrigin"

Value="0.5,0.5"/>

<Setter Property="RenderTransform">

<Setter.Value>

<RotateTransform></RotateTransform>

</Setter.Value>

</Setter>

<Style.Triggers>

<EventTrigger ...

<EventTrigger ...

</Style.Triggers>

</Style>

(34)

Teraz każdy z przycisków będzie używał tej animacji:

<StackPanel>

<Button>jeden</Button>

<Button>dwa</Button>

<Button>trzy</Button>

<Button>cztery</Button>

<Button>pięć</Button>

</StackPanel>

(35)

Jeśli wymienimy RenderTransform na LayoutTransform:

(36)

Aby animować bardziej złożone rodzaje transformacji najlepiej umieścić je w TransformGroup:

<Border.RenderTransform>

<TransformGroup>

<ScaleTransform></ScaleTransform>

<RotateTransform></RotateTransform>

</TransformGroup>

</Border.RenderTransform>

Następnie:

<DoubleAnimation Storyboard.TargetProperty=

"RenderTransform.Children[0].ScaleX" .../>

<DoubleAnimation Storyboard.TargetProperty=

"RenderTransform.Children[0].ScaleY" .../>

<DoubleAnimation Storyboard.TargetProperty=

"RenderTransform.Children[1].Angle" .../>

(37)

Zamiast animowania transformacji elementu, można rozważyć animację prostokąta wypełnionego VisualBrushem:

<Rectangle Name="rectangle">

<Rectangle.Fill>

<VisualBrush Visual="{Binding ElementName=element}"/>

</Rectangle.Fill>

<Rectangle.RenderTransform>

...

</Rectangle.RenderTransform>

</Rectangle>

(38)

Animowanie pędzli

Kolor pędzla animujemy przy pomocy klasy ColorAnimation. Wymaga stworzenia pędzla:

<Ellipse Stretch="Uniform" Margin="3" Name="elipsa">

<Ellipse.Fill>

<RadialGradientBrush>

<GradientStop Color="Yellow" Offset="0"/>

<GradientStop Color="LawnGreen" Offset="1"/>

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

(39)

Teraz możemy ustawić jego właściwości:

<BeginStoryboard>

<Storyboard>

<ColorAnimation Storyboard.TargetName="elipsa"

To="DeepSkyBlue" Storyboard.TargetProperty=

"Fill.GradientStops[1].Color" Duration="0:0:1"

AutoReverse="True" RepeatBehavior="Forever"/>

</Storyboard>

</BeginStoryboard>

(40)

PointAnimation pozwala przemieszczać punkt (działa to jak DoubleAnimation składowej X i Y). Dzięki temu możemy np. modyfikować kształt figury zbudowanej z punktów. W poniższym przykładzie animujemy centralny punkt gradientu radialnego.

<PointAnimation Storyboard.TargetName="elipsa"

Storyboard.TargetProperty="Fill.GradientOrigin"

From="0.7,0.3" To="0.3,0.7" Duration="0:0:1"

AutoReverse="True" RepeatBehavior="Forever" />

(41)

Animacja z wykorzystaniem klatek kluczowych

Pozwala zdefiniować większą liczbę etapów animacji. Każda „klatka” ma określoną wartość docelową oraz czas. Można w ten sposób definiować bardziej złożony przebieg animacji (zmian wartości). Przykład:

<Grid>

<Ellipse Width="20" Height="20" Stroke="Black"

Fill="Yellow" Name="elipsa" />

</Grid>

(42)

Animacja rozmiaru:

<Window.Triggers>

<EventTrigger RoutedEvent="Window.Loaded">

<BeginStoryboard>

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=

"elipsa" Storyboard.TargetProperty="Width"

RepeatBehavior="Forever">

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:0"/>

<LinearDoubleKeyFrame Value="200" KeyTime="0:0:1"/>

<LinearDoubleKeyFrame Value="200" KeyTime="0:0:2"/>

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:3"/>

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:4"/>

</DoubleAnimationUsingKeyFrames>

...

(43)

...

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=

"elipsa" Storyboard.TargetProperty="Height"

RepeatBehavior="Forever">

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:0"/>

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:1"/>

<LinearDoubleKeyFrame Value="200" KeyTime="0:0:2"/>

<LinearDoubleKeyFrame Value="200" KeyTime="0:0:3"/>

<LinearDoubleKeyFrame Value="20" KeyTime="0:0:4"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Window.Triggers>

Klatki określają węzły animacji – punkty, do jakich ma się udać animacja, wartości jakie mamy przyjąć. Dzielą one animację na wykonywane po kolei segmenty.

(44)

Poza interpolacją liniową między klatkami kluczowymi, możemy wybrać wartości dyskretne (nie dokonuje interpolacji – wartość jest przyjmowana w określonej chwili czasu):

<ColorAnimationUsingKeyFrames Storyboard.TargetName="elipsa"

Storyboard.TargetProperty="Fill.Color"

RepeatBehavior="Forever">

<DiscreteColorKeyFrame Value="Yellow" KeyTime="0:0:0"/>

<DiscreteColorKeyFrame Value="Red" KeyTime="0:0:0.3"/>

<DiscreteColorKeyFrame Value="Green" KeyTime="0:0:0.6"/>

<DiscreteColorKeyFrame Value="Blue" KeyTime="0:0:0.9"/>

<DiscreteColorKeyFrame Value="Yellow" KeyTime="0:0:1.2"/>

</ColorAnimationUsingKeyFrames>

Wartość KeyTime (koniec segmentu animacji) może być również określana procentowo.

Możemy używać różnych interpolacji w obrębie jednej animacji.

(45)

Dostępny jest jeszcze jeden rodzaj klatek kluczowych: oparte na krzywej Beziera. Używają interpolacji do zmiany wartości, ale zgodnie z krzywą Beziera. Dwa punkty krzywej są stałe (0,0 i 1,1), pozostałe dwa definiowane są przy pomocy KeySpline.

<Canvas>

<Ellipse Canvas.Top="20" ... Name="elipsa1"/>

<Ellipse Canvas.Top="60" ... Name="elipsa2"/>

</Canvas>

(46)

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=

"elipsa1" Storyboard.TargetProperty="(Canvas.Left)"

RepeatBehavior="Forever">

<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/>

<LinearDoubleKeyFrame Value="300" KeyTime="0:0:4"/>

<LinearDoubleKeyFrame Value="0" KeyTime="0:0:8"/>

</DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=

"elipsa2" Storyboard.TargetProperty="(Canvas.Left)"

RepeatBehavior="Forever">

<SplineDoubleKeyFrame Value="0" KeyTime="0:0:0" />

<SplineDoubleKeyFrame Value="300" KeyTime="0:0:4"

KeySpline="0.5,0 0.5,1"/>

<SplineDoubleKeyFrame Value="0" KeyTime="0:0:8"

KeySpline="0.2,0.5 0.5,0.2"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

(47)

Animation easing

Efekt nieliniowy na początku lub końcu zmiany wartości. Pozwala na bardziej naturalne animacje. Własność może np. łagodniej zbliżać się do wartości docelowej lub oscylować przez jakiś czas wokół niej.

<Storyboard ...>

<DoubleAnimation Storyboard.TargetName="cmdGrow"

Storyboard.TargetProperty="Width" To="400"

Duration="0:0:1.5">

<DoubleAnimation.EasingFunction>

<ElasticEase EasingMode="EaseOut" Oscillations="10"/>

</DoubleAnimation.EasingFunction>

</DoubleAnimation>

</Storyboard>

EasingMode (EaseOut, EaseIn, EaseInOut) określa, czy efekt ma być stosowany na początku czy końcu animacji.

(48)

Dostępne funkcje:

• BackEase – pozwala na „przestrzelenie się” (własność początkowo osiąga wartość przekraczającą docelową), by potem powoli dorównać

• ElasticEase – efekt sprężystości (przestrzelenie i słabnąca oscylacja)

• BounceEase – podobnie jak wyżej, ale bez przestrzelenia

• CircleEase, CubicEase, QuadraticEase, QuarticEase, QuinticEase, SineEase, PowerEase,

• ExponentialEase – steruje początkowym przyśpieszeniem i końcowym zwolnieniem przy użyciu różnych funkcji

Możemy to też stosować do klatek kluczowych – wymaga odpowiedniego typu klatki:

<PointAnimationUsingKeyFrames ... >

<LinearPointKeyFrame .../>

<EasingPointKeyFrame ...>

<EasingPointKeyFrame.EasingFunction>

<CircleEase></CircleEase>

</EasingPointKeyFrame.EasingFunction>

</EasingPointKeyFrame>

<LinearPointKeyFrame .../>

<LinearPointKeyFrame .../>

</PointAnimationUsingKeyFrames>

(49)

Animacje oparte na ścieżkach

Używają geometrii, by określić ustawianą przez animację wartość. Przeważnie stosowane do określania położenia (ale mogą działać dla dowolnych własności – jej wartość

odczytywana jest ze zdefiniowanej ścieżki).

Zaczynamy od zdefiniowania geometrii:

<Window.Resources>

<PathGeometry x:Key="gwiazda"

Figures="M 95,20 L 160,150 L 20,70 L 180,60 L 50,160 Z"/>

...

</Window.Resources>

Następnie możemy jej użyć do animacji położenie elipsy:

<Storyboard>

<DoubleAnimationUsingPath Storyboard.TargetName="elipsa3"

Storyboard.TargetProperty="(Canvas.Left)"

PathGeometry="{StaticResource gwiazda}"

Duration="0:0:5" RepeatBehavior="Forever" Source="X" />

<DoubleAnimationUsingPath Storyboard.TargetName="elipsa3"

Storyboard.TargetProperty="(Canvas.Top)"

PathGeometry="{StaticResource gwiazda}"

Duration="0:0:5" RepeatBehavior="Forever" Source="Y"/>

</Storyboard>

(50)

Efekty

WPF dysponuje zestawem efektów wizualnych, które możemy nadać dowolnemu elementowi. Są dwie grupy efektów: BitmapEffects (mniej wydajne) oraz Effects.

Przykład:

<Label ...>

<Label.BitmapEffect>

<OuterGlowBitmapEffect GlowColor="Yellow"

GlowSize="20" Opacity="0.5"/>

</Label.BitmapEffect>

Hello world!

</Label>

<Button ...>

<Button.Effect>

<BlurEffect Radius="5"/>

</Button.Effect>

OK

</Button>

(51)

BitmapEffects:

• BlurBitmapEffect – rozmycie (własności Radius, KernelType)

• BevelBitmapEffect – efekt wypukłej krawędzi (własności BevelWidth, EdgeProfile, LightAngle, Relief, Smoothness)

• EmbossBitmapEffect – efekt wyżłobienia (własności LightAngle, Relief)

• OuterGlowBitmapEffect – efekt halo (GlowColor, GlowSize, Noise, Opacity)

• DropShadowBitmapEffect – efekt cienia (Color, Direction, Noise, Opacity, ShadowDepth, Softness)

• BitmapEffectGroup – pozwala łączyć kilka efektów Effects:

• BlurEffect – rozmycie (własności Radius, KernelType, RenderingBias)

• DropShadowEffect – efekt cienia (własności BlurRadius, Color, Direction, Opacity, ShadowDepth, RenderingBias)

• ShaderEffect – pozwala zastosować efekt pixel shadera (należy wskazać gotowy, skompilowany plik)

Oczywiście efekty (ich własności) też możemy animować.

(52)

Frame-Based Animation

WPF pozwala również tworzyć własną, dowolną animację przez ręczne generowanie zawartości kolejnych ramek.

Należy obsłużyć statyczne zdarzenie CompositionTarget.Rendering. Będzie ono podnoszone cyklicznie (aby pobrać zawartość ramki do wyrenderowania).

CompositionTarget.Rendering += RenderFrame;

Naszym zadaniem jest dostarczenie zawartości: poprzez manipulację elementami okna, dodawanie nowych, etc.

private void RenderFrame(object sender, EventArgs e) {

...

Ellipse ellipse = new Ellipse();

ellipse.Width = ...;

ellipse.Height = ...;

ellipse.Fill = ...;

canvas.Children.Add(ellipse);

...

}

Cytaty

Powiązane dokumenty

• Element docelowy zawsze aktualizowany jest automatyczne, niezależnie od tego, jak zmieni się źródło}. • Aktualizacja źródła w wyniku zmiany elementu docelowego jest zależna

• W momencie ładowania okna stwórzmy lub załądujmy listę danych i pobierzmy widok:. List&lt;Book&gt; lst =

• Zawartość separatora można zmieniać przy pomocy szablonów – pozwala to definiować własne, nieklikalne elementy menu (nawet jeśli dodamy do menu wrzucimy element nie

• IsColumnWidthFlexible – decyduje, czy kontener może dopasować rozmiar kolumny (jeśli false, użyta jest dokładna wartość ColumnWidth, jeśli true – ColumnWidth to

• IsFilled – jeśli true, wnętrze jest wypełniane przy użyciu pędzla Path.Fill PathFigure to kształt który narysowany jest linią składającą się z wielu

• Jeśli chcemy aby szablony były używane w obrębie wielu aplikacji należy stworzyć osobny słownik zasobów (Resource Dictionary) – Solution Explorer -&gt; Add -&gt;. New

• Positions – kolekcja wszystkich punktów siatki (wierzchołków trójkątów). Często jeden punkt jest wierzchołkiem kilku trójkątów, np: sześcian wymaga 12 trójkątów,

(1)Ustawienie kodowania typu UTF-8 w projektach Java SE oraz Java EE – w zakładce Projects należy prawym klawiszem kliknąć.. na nazwę projektu i wybrać z listy