Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dowiedz się, jak animować grafikę SkiaSharp
Możesz animować grafikę SkiaSharp w programie Xamarin.Forms , powodując PaintSurface , że metoda jest wywoływana okresowo, za każdym razem rysując grafikę nieco inaczej. Poniżej przedstawiono animację pokazaną w dalszej części tego artykułu z koncentrycznymi okręgami, które pozornie rozszerzają się od środka:

Strona Pulsująca elipsa w programie przykładowym animuje dwie osie wielokropka, aby wydawała się pulsować, a nawet kontrolować szybkość tego pulsowania. Plik PulsatingEllipsePage.xaml tworzy wystąpienie elementu Xamarin.FormsSlider i , Label aby wyświetlić bieżącą wartość suwaka. Jest to typowy sposób integrowania elementu SKCanvasView z innymi Xamarin.Forms widokami:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
x:Class="SkiaSharpFormsDemos.PulsatingEllipsePage"
Title="Pulsating Ellipse">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Slider x:Name="slider"
Grid.Row="0"
Maximum="10"
Minimum="0.1"
Value="5"
Margin="20, 0" />
<Label Grid.Row="1"
Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='Cycle time = {0:F1} seconds'}"
HorizontalTextAlignment="Center" />
<skia:SKCanvasView x:Name="canvasView"
Grid.Row="2"
PaintSurface="OnCanvasViewPaintSurface" />
</Grid>
</ContentPage>
Plik będący za kodem tworzy wystąpienie Stopwatch obiektu, który będzie służył jako zegar o wysokiej precyzji. Przesłonięcia OnAppearing ustawia pageIsActive pole na true i wywołuje metodę o nazwie AnimationLoop. Przesłonięcia OnDisappearing ustawia to pageIsActive pole na false:
Stopwatch stopwatch = new Stopwatch();
bool pageIsActive;
float scale; // ranges from 0 to 1 to 0
public PulsatingEllipsePage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
pageIsActive = true;
AnimationLoop();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
pageIsActive = false;
}
Metoda AnimationLoop uruchamia Stopwatch pętle , a następnie wykonuje pętle , gdy pageIsActive ma wartość true. Jest to zasadniczo "nieskończona pętla", gdy strona jest aktywna, ale nie powoduje zawieszenia programu, ponieważ pętla kończy się wywołaniem Task.Delay z operatorem await , co pozwala innym częściom funkcji programu. Argument Task.Delay powoduje jego ukończenie po 1/30 sekundy. Definiuje szybkość klatek animacji.
async Task AnimationLoop()
{
stopwatch.Start();
while (pageIsActive)
{
double cycleTime = slider.Value;
double t = stopwatch.Elapsed.TotalSeconds % cycleTime / cycleTime;
scale = (1 + (float)Math.Sin(2 * Math.PI * t)) / 2;
canvasView.InvalidateSurface();
await Task.Delay(TimeSpan.FromSeconds(1.0 / 30));
}
stopwatch.Stop();
}
Pętla while rozpoczyna się od uzyskania czasu cyklu z .Slider Jest to czas w sekundach, na przykład 5. Druga instrukcja oblicza wartość t czasu. W przypadku wartości cycleTime 5 t zwiększa się z zakresu od 0 do 1 co 5 sekund. Argument Math.Sin funkcji w drugiej instrukcji waha się od 0 do 2π co 5 sekund. Funkcja Math.Sin zwraca wartość z zakresu od 0 do 1 z powrotem do 0, a następnie do –1 i 0 co 5 sekund, ale z wartościami, które zmieniają się wolniej, gdy wartość jest zbliżona do 1 lub –1. Wartość 1 jest dodawana, więc wartości są zawsze dodatnie, a następnie są dzielone przez 2, więc wartości wahają się od 1/2 do 1/2 do 0 do 1/2, ale wolniej, gdy wartość wynosi około 1 i 0. Jest on przechowywany w scale polu i SKCanvasView jest unieważniany.
Metoda PaintSurface używa tej scale wartości do obliczenia dwóch osi wielokropka:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
float maxRadius = 0.75f * Math.Min(info.Width, info.Height) / 2;
float minRadius = 0.25f * maxRadius;
float xRadius = minRadius * scale + maxRadius * (1 - scale);
float yRadius = maxRadius * scale + minRadius * (1 - scale);
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Blue;
paint.StrokeWidth = 50;
canvas.DrawOval(info.Width / 2, info.Height / 2, xRadius, yRadius, paint);
paint.Style = SKPaintStyle.Fill;
paint.Color = SKColors.SkyBlue;
canvas.DrawOval(info.Width / 2, info.Height / 2, xRadius, yRadius, paint);
}
}
Metoda oblicza maksymalny promień na podstawie rozmiaru obszaru wyświetlania i minimalnego promienia na podstawie maksymalnego promienia. Wartość scale jest animowana z zakresu od 0 do 1 do 0, więc metoda używa tej metody do obliczenia wartości xRadius i yRadius , które wahają się między minRadius i maxRadius. Te wartości są używane do rysowania i wypełniania wielokropka:
Zwróć uwagę, że SKPaint obiekt jest tworzony w using bloku. Podobnie jak wiele klas SKPaint SkiaSharp pochodzi z SKObjectklasy , która pochodzi z SKNativeObjectklasy , która implementuje IDisposable interfejs. SKPaint zastępuje metodę Dispose zwalniania niezarządzanych zasobów.
Umieszczenie SKPaint bloku using zapewnia wywołanie Dispose na końcu bloku w celu zwolnienia tych niezarządzanych zasobów. Dzieje się tak, gdy pamięć używana przez SKPaint obiekt jest zwalniana przez moduł odśmiecania pamięci platformy .NET, ale w kodzie animacji najlepiej proaktywnie zwalniać pamięć w bardziej uporządkowany sposób.
Lepszym rozwiązaniem w tym konkretnym przypadku byłoby utworzenie dwóch SKPaint obiektów raz i zapisanie ich jako pól.
To właśnie robi animacja Expanding Circles . Klasa ExpandingCirclesPage rozpoczyna się od zdefiniowania kilku pól, w tym SKPaint obiektu:
public class ExpandingCirclesPage : ContentPage
{
const double cycleTime = 1000; // in milliseconds
SKCanvasView canvasView;
Stopwatch stopwatch = new Stopwatch();
bool pageIsActive;
float t;
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke
};
public ExpandingCirclesPage()
{
Title = "Expanding Circles";
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
...
}
Ten program używa innego podejścia do animacji na Xamarin.FormsDevice.StartTimer podstawie metody . Pole t jest animowane od 0 do 1 co cycleTime milisekundy:
public class ExpandingCirclesPage : ContentPage
{
...
protected override void OnAppearing()
{
base.OnAppearing();
pageIsActive = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
{
t = (float)(stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime);
canvasView.InvalidateSurface();
if (!pageIsActive)
{
stopwatch.Stop();
}
return pageIsActive;
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
pageIsActive = false;
}
...
}
Program PaintSurface obsługi rysuje pięć koncentrycznych okręgów z animowanym promieniem. Jeśli zmienna baseRadius jest obliczana jako 100, to jak t jest animowana z zakresu od 0 do 1, promienie pięciu okręgów zwiększają się z 0 do 100, 100 do 200, 200 do 300, 300 do 400 i 400 do 500. Dla większości okręgów jest strokeWidth 50, ale dla pierwszego koła, strokeWidth animuje się z zakresu od 0 do 50. W przypadku większości okręgów kolor jest niebieski, ale dla ostatniego okręgu kolor jest animowany z niebieskiego na przezroczysty. Zwróć uwagę na czwarty argument SKColor konstruktora, który określa nieprzezroczystość:
public class ExpandingCirclesPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
float baseRadius = Math.Min(info.Width, info.Height) / 12;
for (int circle = 0; circle < 5; circle++)
{
float radius = baseRadius * (circle + t);
paint.StrokeWidth = baseRadius / 2 * (circle == 0 ? t : 1);
paint.Color = new SKColor(0, 0, 255,
(byte)(255 * (circle == 4 ? (1 - t) : 1)));
canvas.DrawCircle(center.X, center.Y, radius, paint);
}
}
}
Wynik polega na tym, że obraz wygląda tak samo, gdy t równa się 0, gdy t równa 1, a okręgi wydają się nadal rozszerzać na zawsze:

