Animazione personalizzata
La classe .NET Multi-platform App UI (.NET MAUI) Animation è il blocco predefinito di tutte le animazioni MAUI .NET, con i metodi di estensione nella ViewExtensions classe che creano uno o più Animation oggetti.
È necessario specificare diversi parametri durante la creazione di un Animation oggetto, inclusi i valori iniziale e finale della proprietà animata e un callback che modifica il valore della proprietà. Un Animation oggetto può anche gestire una raccolta di animazioni figlio che possono essere eseguite e sincronizzate. Per altre informazioni, vedere Animazioni figlio.
L'esecuzione di un'animazione creata con la Animation classe , che può includere o meno animazioni figlio, viene ottenuta chiamando il Commit metodo . Questo metodo specifica la durata dell'animazione e, tra gli altri elementi, un callback che controlla se ripetere l'animazione.
Nota
La Animation classe ha una IsEnabled proprietà che può essere esaminata per determinare se le animazioni sono state disabilitate dal sistema operativo, ad esempio quando viene attivata la modalità risparmio energia.
In Android le animazioni rispettano le impostazioni di animazione di sistema:
- Se le animazioni del sistema sono disabilitate (dalle funzionalità di accessibilità o dalle funzionalità dello sviluppatore), le nuove animazioni passeranno immediatamente allo stato finito.
- Se la modalità di risparmio energia del dispositivo viene attivata mentre le animazioni sono in corso, le animazioni passeranno immediatamente allo stato finito.
- Se le durate di animazione del dispositivo sono impostate su zero (disabilitate) mentre le animazioni sono in corso e la versione dell'API è 33 o successiva, le animazioni passeranno immediatamente allo stato finito.
Creare un'animazione
Quando si crea un Animation oggetto, in genere, sono necessari almeno tre parametri, come illustrato nell'esempio di codice seguente:
var animation = new Animation(v => image.Scale = v, 1, 2);
In questo esempio, un'animazione della Scale proprietà di un'istanza Image viene definita da un valore compreso tra 1 e 2. Il valore animato viene passato al callback specificato come primo argomento, in cui viene usato per modificare il valore della Scale proprietà.
L'animazione viene avviata con una chiamata al Commit metodo :
animation.Commit(this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);
Nota
Il Commit metodo non restituisce un Task
oggetto . Le notifiche vengono invece fornite tramite metodi di callback.
Nel metodo vengono specificati Commit gli argomenti seguenti:
- Il primo argomento (
owner
) identifica il proprietario dell'animazione. Può trattarsi dell'elemento visivo in cui viene applicata l'animazione o di un altro elemento visivo, ad esempio la pagina. - Il secondo argomento (
name
) identifica l'animazione con un nome. Il nome viene combinato con il proprietario per identificare in modo univoco l'animazione. Questa identificazione univoca può quindi essere usata per determinare se l'animazione è in esecuzione (AnimationIsRunning) o per annullarla (AbortAnimation). - Il terzo argomento (
rate
) indica il numero di millisecondi tra ogni chiamata al metodo di callback definito nel Animation costruttore. - Il quarto argomento (
length
) indica la durata dell'animazione, espressa in millisecondi. - Il quinto argomento (Easing) definisce la funzione di interpolazione da usare nell'animazione. In alternativa, la funzione di interpolazione può essere specificata come argomento per il Animation costruttore. Per altre informazioni sulle funzioni di interpolazione, vedere Funzioni di interpolazione.
- Il sesto argomento (
finished
) è un callback che verrà eseguito al termine dell'animazione. Questo callback accetta due argomenti, con il primo argomento che indica un valore finale e il secondo argomentobool
impostato sutrue
se l'animazione è stata annullata. In alternativa, ilfinished
callback può essere specificato come argomento per il Animation costruttore. Tuttavia, con una singola animazione, sefinished
i callback vengono specificati sia nel Animation costruttore Commit che nel metodo , verrà eseguito solo il Commit callback specificato nel metodo . - Il settimo argomento (
repeat
) è un callback che consente di ripetere l'animazione. Viene chiamato alla fine dell'animazione e la restituzionetrue
indica che l'animazione deve essere ripetuta.
Nell'esempio precedente, l'effetto complessivo consiste nel creare un'animazione che aumenta la Scale proprietà di un'istanza Image da 1 a 2 secondi (2000 millisecondi), usando la Linear funzione di interpolazione. Ogni volta che l'animazione viene completata, la relativa Scale proprietà viene reimpostata su 1 e l'animazione viene ripetuta.
Nota
Le animazioni simultanee, eseguite indipendentemente l'una dall'altra, possono essere costruite creando un Animation oggetto per ogni animazione e quindi chiamando il Commit metodo su ogni animazione.
Animazioni figlio
La Animation classe supporta anche animazioni figlio, ovvero Animation oggetti a cui vengono aggiunti altri Animation oggetti come elementi figlio. In questo modo è possibile eseguire e sincronizzare una serie di animazioni. L'esempio di codice seguente illustra la creazione e l'esecuzione di animazioni figlio:
var parentAnimation = new Animation();
var scaleUpAnimation = new Animation(v => image.Scale = v, 1, 2, Easing.SpringIn);
var rotateAnimation = new Animation(v => image.Rotation = v, 0, 360);
var scaleDownAnimation = new Animation(v => image.Scale = v, 2, 1, Easing.SpringOut);
parentAnimation.Add(0, 0.5, scaleUpAnimation);
parentAnimation.Add(0, 1, rotateAnimation);
parentAnimation.Add(0.5, 1, scaleDownAnimation);
parentAnimation.Commit(this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState(true, false));
In alternativa, l'esempio di codice può essere scritto in modo più conciso:
new Animation
{
{ 0, 0.5, new Animation (v => image.Scale = v, 1, 2) },
{ 0, 1, new Animation (v => image.Rotation = v, 0, 360) },
{ 0.5, 1, new Animation (v => image.Scale = v, 2, 1) }
}.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
In entrambi gli esempi viene creato un oggetto padre Animation , a cui vengono quindi aggiunti oggetti aggiuntivi Animation . I primi due argomenti del Add metodo specificano quando iniziare e completare l'animazione figlio. I valori dell'argomento devono essere compresi tra 0 e 1 e rappresentano il periodo relativo all'interno dell'animazione padre che l'animazione figlio specificata sarà attiva. Pertanto, in questo esempio sarà scaleUpAnimation
attivo per la prima metà dell'animazione, sarà scaleDownAnimation
attivo per la seconda metà dell'animazione e rotateAnimation
sarà attivo per l'intera durata.
L'effetto complessivo di questo esempio è che l'animazione si verifica in più di 4 secondi (4000 millisecondi). Anima scaleUpAnimation
la Scale proprietà da 1 a 2 secondi. L'oggetto scaleDownAnimation
anima quindi la Scale proprietà da 2 a 1, oltre 2 secondi. Mentre si verificano entrambe le animazioni di scala, la rotateAnimation
Rotation proprietà viene animata da 0 a 360, oltre 4 secondi. Entrambe le animazioni di ridimensionamento usano anche funzioni di interpolazione. La SpringIn funzione di interpolazione fa sì che l'istanza Image venga inizialmente ridotta prima di aumentare le dimensioni e la SpringOut funzione di interpolazione fa sì che la Image dimensione effettiva diventi inferiore alla fine dell'animazione completa.
Esistono diverse differenze tra un Animation oggetto che usa animazioni figlio e una che non:
- Quando si usano animazioni figlio, il
finished
callback su un'animazione figlio indica quando l'elemento figlio è stato completato e il callback passato al Commit metodo indica quandofinished
l'intera animazione è stata completata. - Quando si usano animazioni figlio, la restituzione
true
dalrepeat
callback sul Commit metodo non causerà la ripetizione dell'animazione, ma l'animazione continuerà a essere eseguita senza nuovi valori. - Quando si include una funzione di interpolazione nel Commit metodo e la funzione di interpolazione restituisce un valore maggiore di 1, l'animazione verrà terminata. Se la funzione di interpolazione restituisce un valore minore di 0, il valore viene bloccato su 0. Per usare una funzione di interpolazione che restituisce un valore minore di 0 o maggiore di 1, è necessario specificarla in una delle animazioni figlio, anziché nel Commit metodo .
La Animation classe include WithConcurrent anche metodi che possono essere usati per aggiungere animazioni figlio a un oggetto padre Animation . Tuttavia, i valori begin
degli argomenti e finish
non sono limitati a 0 a 1, ma solo quella parte dell'animazione figlio che corrisponde a un intervallo compreso tra 0 e 1 sarà attiva. Ad esempio, se una WithConcurrent chiamata al metodo definisce un'animazione figlio destinata a una Scale proprietà da 1 a 6, ma con begin
i valori e finish
-2 e 3, il begin
valore di -2 corrisponde a un Scale valore 1 e il finish
valore di 3 corrisponde a un Scale valore 6. Poiché i valori non compresi nell'intervallo 0 e 1 non fanno parte di un'animazione, la Scale proprietà verrà animata solo da 3 a 6.
Annullare un'animazione
Un'app può annullare un'animazione personalizzata con una chiamata al AbortAnimation metodo di estensione:
this.AbortAnimation ("SimpleAnimation");
Poiché le animazioni sono identificate in modo univoco da una combinazione del proprietario dell'animazione e dal nome dell'animazione, il proprietario e il nome specificati durante l'esecuzione dell'animazione devono essere specificati per annullarlo. Di conseguenza, questo esempio annulla immediatamente l'animazione denominata SimpleAnimation
di proprietà della pagina.
Creare un'animazione personalizzata
Gli esempi mostrati finora hanno dimostrato animazioni che potrebbero essere ugualmente ottenute con i metodi nella ViewExtensions classe . Tuttavia, il vantaggio della Animation classe è che ha accesso al metodo di callback, che viene eseguito quando il valore animato cambia. In questo modo il callback può implementare qualsiasi animazione desiderata. L'esempio di codice seguente, ad esempio, anima la BackgroundColor proprietà di una pagina impostandola su Color valori creati dal Color.FromHsla
metodo , con valori di tonalità compresi tra 0 e 1:
new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5),
start: 0,
end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Colors.Black);
L'animazione risultante fornisce l'aspetto di far avanzare lo sfondo della pagina attraverso i colori dell'arcobaleno.
Creare un metodo di estensione dell'animazione personalizzata
I metodi di estensione nella ViewExtensions classe animano una proprietà dal relativo valore corrente a un valore specificato. Ciò rende difficile creare, ad esempio, un ColorTo
metodo di animazione che può essere usato per animare un colore da un valore a un altro. Ciò è dovuto al fatto che controlli diversi hanno proprietà diverse di tipo Color. Mentre la VisualElement classe definisce una BackgroundColor proprietà, questa non è sempre la proprietà desiderata Color
da animare.
La soluzione a questo problema consiste nel non avere il ColorTo
metodo come destinazione di una determinata Color
proprietà. Al contrario, può essere scritto con un metodo di callback che passa di nuovo il valore interpolato Color al chiamante. Inoltre, il metodo accetta argomenti di inizio e fine Color .
Il ColorTo
metodo può essere implementato come metodo di estensione che usa il Animate metodo nella AnimationExtensions classe per fornire la relativa funzionalità. Ciò è dovuto al fatto che il Animate metodo può essere usato per impostare come destinazione proprietà che non sono di tipo double
, come illustrato nell'esempio di codice seguente:
public static class ViewExtensions
{
public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
{
Func<double, Color> transform = (t) =>
Color.FromRgba(fromColor.Red + t * (toColor.Red - fromColor.Red),
fromColor.Green + t * (toColor.Green - fromColor.Green),
fromColor.Blue + t * (toColor.Blue - fromColor.Blue),
fromColor.Alpha + t * (toColor.Alpha - fromColor.Alpha));
return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
}
public static void CancelAnimation(this VisualElement self)
{
self.AbortAnimation("ColorTo");
}
static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
{
easing = easing ?? Easing.Linear;
var taskCompletionSource = new TaskCompletionSource<bool>();
element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
return taskCompletionSource.Task;
}
}
Il Animate metodo richiede un transform
argomento, ovvero un metodo di callback. L'input per questo callback è sempre compreso double
tra 0 e 1. Pertanto, in questo esempio il ColorTo
metodo definisce la propria trasformazione Func
che accetta un double
valore compreso tra 0 e 1 e che restituisce un Color valore corrispondente a tale valore. Il Color valore viene calcolato interpolando i Red
valori , Green
, Blue
e Alpha
dei due argomenti forniti Color . Il Color valore viene quindi passato al metodo di callback da applicare a una proprietà. Questo approccio consente al ColorTo
metodo di animare qualsiasi proprietà specificata Color :
await Task.WhenAll(
label.ColorTo(Colors.Red, Colors.Blue, c => label.TextColor = c, 5000),
label.ColorTo(Colors.Blue, Colors.Red, c => label.BackgroundColor = c, 5000));
await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
await boxView.ColorTo(Colors.Blue, Colors.Red, c => boxView.Color = c, 4000);
In questo esempio di codice il ColorTo
metodo anima le TextColor
proprietà e BackgroundColor di un Labeloggetto , la BackgroundColor proprietà di una pagina e la Color
proprietà di un oggetto BoxView.