Animazioni e transizioni rappresentano una componente essenziale di qualsiasi user experience, in quanto garantiscono la fluidità della user interface e al tempo stesso aiutano l'utente a comprendere cosa sta accadendo sullo schermo. È importante non eccedere nelle animazioni, per non distrarre o, peggio ancora, irritare l'utente. Usate con giudizio, animazioni e transizioni rappresentano una parte integrante dell'ecosistema di Windows 8 e possono contribuire significativamente al successo delle nostre app.
Sebbene i termini "transizione" e "animazione" vengano generalmente usati in modo interscambiabile, esiste tuttavia una leggera differenza:
- Transizioni: si basano su una serie di movimenti e di effetti predefiniti, su cui lo sviluppatore ha poco o nessun controllo;
- Animazioni: permettono una maggiore flessibilità, grazie all'uso di key frame, storyboard e funzioni di interpolazione.
In questo articolo, vedremo come creare animazioni fluide per le nostre applicazioni Windows Store; nel prossimo articolo vedremo invece come sfruttare l'Animation Library di Microsoft, un set di transizioni predefinite messe a disposizione dello sviluppatore per migliorare, in modo semplice e trasparente, la user experience della propria app.
Creare e personalizzare uno storyboard
Uno storyboard permette di animare elementi della UI modificando il valore di una dependency property in funzione del tempo (Una dependency property rappresenta un tipo di proprietà il cui valore viene tracciato dal "dedicated property system" che fa parte del Windows Runtime. In particolare, come il termine "dependency" suggerisce, il valore di una dependency property dipende dal valore di altri input, come preferenze dell'utente, proprietà just-in-time, risorse, stili, e così via. Per maggiori informazioni su questo argomento si rinvia alla documentazione MSDN).
È possibile creare uno storyboard direttamente in XAML, oppure sfruttando tool come Microsoft Blend per Visual Studio, che permette di concentrarsi sugli aspetti visivi, lasciando allo strumento il compito di produrre il codice XAML corrispondente. È anche possibile creare un'animazione programmaticamente, tramite codice C#, ma questo approccio può facilmente condurre a scrivere codice complesso e scarsamente leggibile.
Quale che sia la strada prescelta, qualunque storyboard poggia sulla classe Storyboard
, la quale deriva a sua volta dalla classe base Timeline
(dalla quale derivano anche tutti gli altri tipi di animazioni e transizioni, incluse quelle comprese nell'Animation Library).
Il codice XAML che segue mostra una semplice animazione che sfrutta uno storyboard per modificare il valore della proprietà Opacity
del controllo Ellipse
da 1.0 (opaco) a 0 (trasparente) nell'intervallo di tempo specificato dalla proprietà Duration
, per poi ripartire da capo, come indicato nella proprietà RepeatBehavior
.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MySimpleStoryboard">
<DoubleAnimation Storyboard.TargetName="BlinkingCircle" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" RepeatBehavior="Forever"/>
</Storyboard>
</Grid.Resources>
<Ellipse x:Name="BlinkingCircle" Width="100" Height="100" Fill="Violet" />
</Grid>
</Page>
Poiché che in questo caso la proprietà che vogliamo modificare è di tipo Double
, l'animazione sfrutta la classe DoubleAnimation
per determinare i valori intermedi della proprietà Opacity
al passare del tempo (interpolazione). Solo alcuni tipi possono essere interpolati dal framework: Double
, Point
e Color
(vedremo come animare proprietà di tipo Object
più avanti). La logica necessaria a interpolare questi tipi è incapsulata, rispettivamente, nelle classi DoubleAnimation
, PointAnimation
e ColorAnimation
.
La proprietà RepeatBehavior
consente di specificare se e quante volte l'animazione deve essere ripetuta dall'inizio. Questa proprietà accetta i seguenti valori:
- Un numero intero che specifica quante volte ripetere l'animazione.
- Un oggetto di tipo
TimeSpan
che indica la durata complessiva dell'animazione. - Lo speciale valore "Forever", che indica di ripetere l'animazione indefinitamente.
Lo storyboard viene collegato a un controllo specifico tramite la proprietà TargetName
, mentre la proprietà TargetProperty
permette di indicare la proprietà di quel controllo che deve essere animata (in questo caso la proprietà Opacity
). Il valore iniziale della proprietà target può essere impostato tramite la proprietà From
(se questa proprietà viene omessa, come valore di partenza verrà utilizzato il valore originale del controllo XAML), mentre il valore finale (ossia il valore che la proprietà assumerà al termine dell'animazione) viene indicato tramite la proprietà To
.
Una volta definita lo storyboard tramite codice XAML, non ci resta che avviare l'animazione. Il modo più semplice per far questo è chiamare il metodo Begin
della classe Storyboard
dal code-behind della pagina, come mostrato nel seguente snippet:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MySimpleStoryboard.Begin();
}
Il prossimo snippet mostra un esempio di storyboard che sfrutta la classe ColorAnimation
per cambiare il colore del controllo Ellipse
al passare del tempo.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyColorStoryboard">
<ColorAnimation Storyboard.TargetName="MyColoredCircle" Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" To="OrangeRed" AutoReverse="True" Duration="0:0:3" RepeatBehavior="Forever"/>
</Storyboard>
</Grid.Resources>
<Ellipse x:Name="MyColoredCircle" Width="100" Height="100" Fill="Yellow" />
</Grid>
</Page>
In questo caso, la proprietà target non è semplicemente indicata con il suo nome, come nel caso dell'opacità. Questo è dovuto al fatto che la proprietà che stiamo cercando di modificare, ossia Color
, è in realtà inserita all'interno di una relazione di tipo "oggetto-proprietà". Nel codice proposto, ad esempio, non è possibile passare direttamente un nuovo colore alla proprietà Fill
della classe Ellipse
(ereditata dalla classe Shape, da cui la prima deriva), perché questa è di tipo Brush
(e non Color
). Per poter impostare un nuovo colore, è dunque necessario proseguire lungo la catena per raggiungere il giusto tipo di proprietà (Double
, Point
o, come in questo caso, Color
). Per raggiungere risultato, viene usata una sintassi nota come "property path syntax":
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
Nell'esempio proposto, le due proprietà, Ellipse.Fill
e SolidColorBrush.Color
, racchiuse tra due parentesi tonde, sono tra di loro in una relazione tale per cui la proprietà Fill
dell'oggetto Ellipse
è di tipo SolidColorBrush
, la quale a sua volta, espone la proprietà Color
(che è quella che ci interessa cambiare). Il punto che connette i due gruppi di parentesi è denominato "step", perché permette di "scendere" all'interno dell'object model della prima proprietà, Ellipse.Fill
, per raggiungere la sub-proprietà SolidColorBrush.Color
.
Nel caso in cui una delle proprietà coinvolte nell'animazione sia una collezione, è possibile usare un indexer per specificare quale elemento della collezione rappresenta il target di quella specifica animazione. Il seguente snippet, ad esempio, punta ad animare il colore del primo GradientStop
della collezione:
Storyboard.TargetProperty = "(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
In uno storyboard è possibile manipolare anche più di una proprietà contemporaneamente. Il codice seguente, ad esempio, modifica sia l'opacità che il colore dell'ellissi:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyColorStoryboard">
<DoubleAnimation Storyboard.TargetName="MyColoredCircle" Storyboard.TargetProperty="Opacity" From="1.0" To="0" Duration="0:0:3" RepeatBehavior="Forever"/>
<ColorAnimation Storyboard.TargetName="MyColoredCircle" Storyboard.TargetProperty="(Shape.Fill).(SolidBrushColor.Color)" To="OrangeRed" Duration="0:0:3" RepeatBehavior="Forever"/>
</Storyboard>
</Grid.Resources>
<Ellipse x:Name="MyColoredCircle" Width="100" Height="100" Fill="Yellow" />
</Grid>
Dependent animation vs. independent animation
Negli esempi visti finora, uno storyboard di un'applicazione Windows Store in C# o C++ non si differenzia da analoghe animazioni in Windows Presentation Foundation (WPF) o Silverlight. In realtà, esistono alcune differenze tutt'altro che marginali, sulle quali conviene adesso soffermarsi.
In primo luogo, infatti, in un'applicazione Windows Store la maggior parte dei controlli XAML introdotti da WinRT arrivano accompagnati da specifiche animazioni, che fanno parte integrante del loro comportamento. La seconda differenza è che, almeno per il momento, WinRT non supporta funzioni di interpolazione (o "easing function") custom. In terzo luogo, in un'app Windows Store gli sviluppatori possono sfruttare, grazie all'Animation Library (discussa più avanti) una serie di transizioni predefinite "out-of-the-box", in modo da non dover ogni volta ricreare animazioni comunemente usate, garantendo al contempo la coerenza della user experience.
La differenza più significativa è tuttavia rappresentata dal fatto che WinRT impedisce l'esecuzione di animazioni che possono creare problemi di performance a livello di UI. La ragione di questo comportamento è semplice: animazioni e transizioni vengono elaborate dal thread di UI, ossia dal thread responsabile del rendering a schermo. Nel framework XAML è disponibile anche un altro thread, denominato "composition thread", il quale è dedicato, come il nome suggerisce, a comporre e ad animare gli elementi visuali prima di disegnarli a schermo.
Il fatto di avere due thread separati per precalcolare il layout di una pagina, da un lato, e per effettuare il rendering a schermo, dall'altro, permette di mantenere un frame rate più elevato e dunque una user interface più fluida, anche in presenza di layout complessi. Quando cominciamo ad animare gli elementi di UI, tuttavia, le cose tendono a complicarsi.
Infatti, se l'animazione riguarda elementi la cui modifica incide sul modo in cui gli altri elementi presenti sulla scena sono visualizzati, il composition thread non può calcolare il layout senza input aggiuntivi da parte del thread di UI. Quest'ultimo, a sua volta, è obbligato a ridisegnare intere aree dello schermo a intervalli di tempo regolari (e molto brevi) fra ogni refresh per catturare l'ultimo valore della proprietà da animare. In questi casi si parla appunto di "animazioni dipendenti" (o "dependent animation") e, come riportato nella documentazione ufficiale MSDN, questo tipo di animazioni ha un costo in termini di responsività e fluidità della UI. Per questa ragione, è bene usare questo tipo di animazioni solo se davvero necessarie (e comunque con grande cautela).
Perché un'animazione dipendente possa essere eseguita, occorre impostare la proprietà EnableDependentAnimation
a true
(per default, questa proprietà è impostata a false
), come mostrato nel prossimo snippet.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyColorStoryboard">
<DoubleAnimation Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Rectangle.Width" From="100" To="300" Duration="0:0:3" RepeatBehavior="Forever" EnableDependentAnimation="True"/>
</Storyboard>
</Grid.Resources>
<Rectangle x:Name="MyRectangle" Width="100" Height="100" Fill="Orange" />
</Grid>
</Page>
Se omettessimo di impostare la proprietà EnableDependentAnimation
a true
, questa animazione non verrebbe eseguita, poiché cambiare la proprietà target (Width
) nel tempo richiede un lavoro addizionale da parte del thread di UI e il sistema impedirebbe l'esecuzione dell'animazione, a meno che non sia espressamente abilitata.
Animazione indipendente
Una animazione indipendente, invece, può essere calcolata direttamente dal composition thread, senza pesare sul thread di UI e garantendo la fluidità e la responsività della user experience. La distinzione fra i due tipi di animazione dipende da numerosi fattori. Ad esempio, ci sono animazioni che, sebbene impattino sul layout della UI, hanno comunque un impatto minimo sul thread di UI e quindi possono essere gestite dal composition thread come se fossero animazioni indipendenti.
Più nel dettaglio, un'animazione è considerata indipendente se presenta una qualunque delle seguenti caratteristiche:
- La durata dell'animazione è pari a zero ("zero-duration animations").
- L'animazione riguarda la proprietà
Opacity
di un oggetto di tipoUIElement.
- L'animazione riguarda i valori delle proprietà
RenderTransform
,Projection
, eClip
di un'oggetto di tipoUIElement
. - L'animazione riguarda le proprietà
Canvas.Left
oCanvas.Top
- L'animazione riguarda la proprietà
SolidColorBrush.Color
dell'oggetto che si vuole animare - L'animazione riguarda proprietà di tipo Object (questo aspetto è discusso più avanti)
Creare animazioni con key frame
A volte è necessario creare storyboard più complesse, magari basate su comportamenti non lineari. In questo caso, è possibile usare key frame e funzioni di interpolazione per controllare fin nei minimi dettagli lo svolgimento dell'animazione.
Un key frame rappresenta uno specifico punto lungo la linea temporale (timeline) dell'animazione e permette di specificare un valore intermedio per la medesima proprietà. Il valore intermedio assegnato a ciascun key frame viene utilizzato come valore di partenza per il successivo key frame nella sequenza temporale, fino a quando viene raggiunto l'ultimo key frame e impostato il valore finale della proprietà. Il codice seguente illustra questi concetti.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyStoryboard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BlinkingEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidBrushColor.Color)">
<LinearColorKeyFrame Value="Violet" KeyTime="0:0:0" />
<LinearColorKeyFrame Value="Blue" KeyTime="0:0:2" />
<LinearColorKeyFrame Value="Green" KeyTime="0:0:4" />
</ColorAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="BlinkingEllipse" Storyboard.TargetProperty="Opacity" AutoReverse="True">
<LinearDoubleKeyFrame Value="1.0" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:2" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Ellipse x:Name="BlinkingEllipse" Width="100" Height="100" Fill="Orange" />
</Grid>
</Page>
La prima cosa da notare sono le classi che incapsulano la logica necessaria ad animare una proprietà attraverso una serie di key frame. Nella prima animazione, ad esempio, il codice usa la classe ColorAnimationUsingKeyFrames
per modificare la proprietà Color dell'oggetto SolidColorBrush
passato alla proprietà Fill
tramite la property path syntax, mentre per animare la proprietà Opacity
del controllo target, che è di tipo Double
, la seconda animazione utilizza la classe DoubleAnimationUsingKeyFrames
. Altre classi disponibili sono PointAnimationUsingKeyFrames
, nel caso in cui la proprietà target sia di tipo Point
, e ObjectAnimationUsingKeyFrames
, utilizzata per animare proprietà di tipo Object
.
La durata di un'animazione con key frame è indicato dalla proprietà KeyTime
dell'ultimo key frame (ossia 4 secondi, nel nostro esempio). È anche possibile impostare in modo esplicito la durata dell'animazione tramite la proprietà Duration
. Tieni tuttavia presente che se la durata impostata tramite questa proprietà è minore dalla durata indicata nell'ultimo key frame, la parte eccedente dell'animazione risulterà troncata. Nel caso in cui non sia presente un key frame con la proprietà KeyTime
impostata a 0:0:0, il valore inziale della proprietà da animare sarà quello definito nel codice XAML del relativo controllo (nell'esempio, mancando il primo key-frame, il colore iniziale dell'ellisse sarebbe arancione).
Dal momento che tutti i tipi di key frame (DoubleAnimationUsingKeyFrames
, ColorAnimationUsingKeyFrames
, e così via) derivano dalla classe base Timeline
, essi ereditano tutti i metodi esposti da quest'ultima, come la proprietà AutoReverse
, che permette di riprodurre al contrario l'animazione, una volta raggiunta la fine (di fatto raddoppiando la sua durata), e la già discussa proprietà RepeatBehavior
.
Funzioni di interpolazione
Un key frame permette anche di usare differenti funzioni matematiche per inferire, dati il valore iniziale e quello finale di una proprietà, anche i valori intermedi lungo la timeline (funzioni di interpolazione, o "easying function").
Esistono diversi tipi di funzioni di interpolazione che è possibile applicare alle proprie animazioni. La logica necessaria a determinare i valori intermedi tra due key frame è incapsulata in classi differenti, a seconda del tipo di interpolazione.
Il metodo più semplice è rappresentato dall'interpolazione lineare, che consiste nell'unire i due valori, quello minimo e quello massimo, con una linea retta: ogni punto di questa retta indica il valore intermedio che la proprietà assumerà in un certo istante. A seconda del tipo di proprietà da animare mediante interpolazione lineare, WinRT mette a disposizione tre classi:
Classe | Usata per |
---|---|
LinearColorKeyFrame | interpolazione lineare di proprietà di tipo Color |
LinearDoubleKeyFrame | interpolazione lineare di proprietà di tipo Double |
LinearPointKeyFrame | interpolazione lineare di proprietà di tipo Point |
Un secondo tipo di interpolazione è quella cosiddetta "spline", la quale crea una transizione non lineare tra due key frame sulla base di una curva di Bézier, che descrive l'accelerazione dell'animazione lungo la timeline. A seconda del tipo di proprietà da animare tramite interpolazione "spline", le classi da utilizzare sono le seguenti:
Classe | Usata per |
---|---|
SplineColorKeyFrame | interpolazione spline di proprietà di tipo Color |
SplineDoubleKeyFrame | interpolazione spline di proprietà di tipo Color |
SplinePointKeyFrame | interpolazione spline di proprietà di tipo Color |
La proprietà KeySpline, comune a tutte e tre queste classi, permette di indicare i due punti di controllo di una curva di Beziér; ciascuno di questi due punti è definito da una coppia di valori di tipo Double
, separati internamente da una virgola. Entrambi questi valori devono essere compresi tra 0 e 1 (inclusi). Le coppie di valori che rappresentano i due punti di controllo sono quindi divise da uno spazio, come mostrato qui di seguito:
KeySpline="0.6,0.0 0.9,0.00"
Il prossimo listato mostra un esempio di interpolazione spline applicata a un ellissi che si muove lungo il suo asse verticale. All'inizio il movimento è lento, per poi accelerare fino a quando la proprietà Canvas.Top
raggiunge il valore di 500 e quindi torna indietro, dapprima veloce, per poi decelerare. Vedremo più avanti come rendere questo comportamento più realistico tramite funzioni di interpolazione più sofisticate.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyBouncingBall">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="MyMovingCircle" Storyboard.TargetProperty="(Canvas.Top)" AutoReverse="True" RepeatBehavior="Forever">
<SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="500" KeyTime="0:0:6" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Canvas>
<Ellipse Fill="Orange" Canvas.Top="0" x:Name="MyMovingCircle" Opacity="1.0" Width="200" Height="200"/>
</Canvas>
</Grid>
</Page>
Oltre all'interpolazione lineare e spline, WinRT mette a disposizione anche una serie di funzioni di interpolazione predefinite che consentono particolari tipi di animazione, senza tuttavia richiedere allo sviluppatore alcuna particolare conoscenza matematica. Queste particolari funzione di interpolazione sono esposte tramite le seguenti classi (le relative definizioni sono prese dalla documentazione ufficiale su MSDN, a cui si rimanda per l'approfondimento delle singole classi):
Interpolazione | Descrizione |
---|---|
BackEase | contrae leggermente il movimento di un'animazione prima che inizi ad animarsi lungo il percorso indicato. |
BounceEase | crea un effetto di rimbalzo. |
CircleEase | crea un'animazione che accelera e/o decelera utilizzando una funzione circolare. |
CubicEase | crea un'animazione che accelera e/o decelera utilizzando la formula f(t) = t3 . |
ElasticEase | crea un'animazione simile a una molla che oscilla avanti e indietro fino a quando non si ferma. |
ExponentialEase | crea un'animazione che accelera e/o decelera utilizzando una formula esponenziale. |
PowerEase | crea un'animazione che accelera e/o decelera utilizzando la formula f(t) = tp , dove p equivale alla proprietà Power . |
QuadraticEase | crea un'animazione che accelera e/o decelera utilizzando la formula f(t) = t2 . |
QuarticEase | crea un'animazione che accelera e/o decelera utilizzando la formula f(t) = t4 . |
QuinticEase | crea un'animazione che accelera e/o decelera utilizzando la formula f(t) = t5 . |
SineEase | crea un'animazione che accelera e/o decelera utilizzando una formula sinusoidale. |
Alcune di queste di queste classi espongono proprietà che permettono di variare il comportamento delle relative funzioni di interpolazione. Ad esempio, la classe BounceEase
, che crea un effetto di rimbalzo, espone due proprietà, Bounces
e Bounciness
, che indicano, rispettivamente, il numero di rimbalzi da includere nell'animazione e il grado di "bounciness", ossia la perdita di altezza tra un rimbalzo e l'altro (maggiore è il valore, più i rimbalzi risulteranno smorzati). Il seguente codice utilizza questa funzione di interpolazione in combinazione a una funzione di tipo a CubicEase
function, mentre Il numero di rimbalzi è impostato a cinque.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyBouncingBall">
<DoubleAnimationUsingKeyFrames Duration="0:0:10" Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="MyMovingCircle" EnableDependentAnimation="True">
<EasingDoubleKeyFrame Value="0" KeyTime="00:00:02">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame Value="500" KeyTime="00:00:06">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase Bounces="5"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Canvas>
<Ellipse Fill="Orange" Canvas.Top="0" x:Name="MyMovingCircle" Opacity="1.0" Width="200" Height="200"/>
</Canvas>
</Grid>
</Page>
È da notare la proprietà EasingFunction
nella precedente definizione XAML; questa proprietà permette di specificare quale funzione di interpolazione utilizzare per l'animazione.
Queste funzioni di interpolazione predefinite possono essere applicate anche ad applicazioni prive di key frame, sempre sfruttando la proprietà EasingFunction
delle classi ColorAnimation
, DoubleAnimation
e PointAnimation
già viste in precedenza. Il prossimo snippet ne mostra un esempio.
<Page
x:Class="Demo.Html.it.UsingAnimations.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.UsingAnimations.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="MyBouncingBall">
<DoubleAnimation From="30" To="200" Duration="00:00:3"
Storyboard.TargetName="MyCircle"
Storyboard.TargetProperty="(Canvas.Top)">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="5" EasingMode="EaseOut"
Bounciness="2" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Grid.Resources>
<Canvas>
<Ellipse Fill="Orange" Canvas.Top="0" x:Name="MyCircle" Opacity="1.0" Width="200" Height="200"/>
</Canvas>
</Grid>
</Page>
Animare proprietà di tipo Object
WinRT espone un tipo speciale di animazione, denominato "discrete key-frame animation", il quale, a differenza degli altri tipi di animazione visti finora, non applica alcun tipo di interpolazione: quando viene raggiunto un nuovo key frame, il nuovo valore viene semplicemente applicator, senza alcun tipo di transizione.
Questo tipo di animazione viene usato per proprietà che non sono di tipo Double
, Color
, or Point
(per ciascuno di questi tipi, infatti, WinRT mette a disposizione classi specifiche), quanto piuttosto di tipo Object
. La logica che regola questo tipo di animazione è incapsulata nella classe ObjectAnimationUsingKeyFrames
. La ragione per cui non è applicata alcuna interpolazione è piuttosto semplice: trattandosi di una reference a un generico tipo Object
, non è possibile interpolare i valori intermedi, ma solo applicare valori "discreti", rendendo l'animazione "a scatti". L'unico modo di ridurre questi bruschi salti è quello di aumentare il numero di key frame lungo la timeline, rendendo la transizione da un valore all'altro più graduale.
Dal momento che in questo caso abbiamo a che fare con un solo tipo di proprietà (Object
) e che non è possibile applicare alcuna funzione di interpolazione, esiste un solo tipo di key frame che può essere utilizzato con questo genere di animazioni, rappresentato dalla classe DiscreteObjectKeyFrame
.
Il file di default denominato StandardStyles.xaml, automaticamente aggiunto da Visual Studio a ogni progetto Windows Store, presenta numerosi esempi di animazione "discreta". Il seguente snippet, che usa la classe ObjectAnimationUsingKeyFrames
per impostare la visibilità iniziale di un elemento di UI, è è preso direttamente da questo file:
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>