Uwaga
Dostęp do tej strony wymaga autoryzacji. Może 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 SKObject
klasy , która pochodzi z SKNativeObject
klasy , 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: