Partilhar via


Pontos e traços no SkiaSharp

Domine os meandros do desenho de linhas pontilhadas e tracejadas em SkiaSharp

SkiaSharp permite desenhar linhas que não são sólidas, mas são compostas de pontos e traços:

Linha pontilhada

Você faz isso com um efeito de caminho, que é uma instância da SKPathEffect classe que você definiu para a PathEffect propriedade de SKPaint. Você pode criar um efeito de caminho (ou combinar efeitos de caminho) usando um dos métodos de criação estática definidos pelo SKPathEffect. SKPathEffect( é um dos seis efeitos suportados pelo SkiaSharp; os outros são descritos na seção SkiaSharp Effect.)

Para desenhar linhas pontilhadas ou tracejadas, use o SKPathEffect.CreateDash método estático. Há dois argumentos: este primeiro é uma matriz de float valores que indicam os comprimentos dos pontos e traços e o comprimento dos espaços entre eles. Essa matriz deve ter um número par de elementos e deve haver pelo menos dois elementos. (Pode haver zero elementos na matriz, mas isso resulta em uma linha sólida.) Se houver dois elementos, o primeiro é o comprimento de um ponto ou traço, e o segundo é o comprimento da lacuna antes do próximo ponto ou traço. Se houver mais de dois elementos, eles estão nesta ordem: comprimento do traço, comprimento do intervalo, comprimento do traço, comprimento do intervalo e assim por diante.

Geralmente, convém tornar os comprimentos de traço e intervalo um múltiplo da largura do traçado. Se a largura do traçado for de 10 pixels, por exemplo, a matriz { 10, 10 } desenhará uma linha pontilhada onde os pontos e lacunas têm o mesmo comprimento que a espessura do traçado.

No entanto, a StrokeCapSKPaint configuração do objeto também afeta esses pontos e traços. Como você verá em breve, isso tem um impacto nos elementos dessa matriz.

Linhas pontilhadas e tracejadas são demonstradas na página Pontos e traços . O arquivo DotsAndDashesPage.xaml instancia dois Picker modos de exibição, um para permitir que você selecione uma tampa de traçado e o segundo para selecionar uma matriz de traço:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
             xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Paths.DotsAndDashesPage"
             Title="Dots and Dashes">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Picker x:Name="strokeCapPicker"
                Title="Stroke Cap"
                Grid.Row="0"
                Grid.Column="0"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKStrokeCap}">
                    <x:Static Member="skia:SKStrokeCap.Butt" />
                    <x:Static Member="skia:SKStrokeCap.Round" />
                    <x:Static Member="skia:SKStrokeCap.Square" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Picker x:Name="dashArrayPicker"
                Title="Dash Array"
                Grid.Row="0"
                Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type x:String}">
                    <x:String>10, 10</x:String>
                    <x:String>30, 10</x:String>
                    <x:String>10, 10, 30, 10</x:String>
                    <x:String>0, 20</x:String>
                    <x:String>20, 20</x:String>
                    <x:String>0, 20, 20, 20</x:String>
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                PaintSurface="OnCanvasViewPaintSurface"
                                Grid.Row="1"
                                Grid.Column="0"
                                Grid.ColumnSpan="2" />
    </Grid>
</ContentPage>

Os três primeiros itens no dashArrayPicker pressupõem que a largura do traçado é de 10 pixels. A matriz { 10, 10 } é para uma linha pontilhada, { 30, 10 } é para uma linha tracejada e { 10, 10, 30, 10 } é para uma linha dot-and-dash. (Os outros três serão discutidos em breve.)

O DotsAndDashesPage arquivo code-behind contém o PaintSurface manipulador de eventos e algumas rotinas auxiliares para acessar os Picker modos de exibição:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Blue,
        StrokeWidth = 10,
        StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem,
        PathEffect = SKPathEffect.CreateDash(GetPickerArray(dashArrayPicker), 20)
    };

    SKPath path = new SKPath();
    path.MoveTo(0.2f * info.Width, 0.2f * info.Height);
    path.LineTo(0.8f * info.Width, 0.8f * info.Height);
    path.LineTo(0.2f * info.Width, 0.8f * info.Height);
    path.LineTo(0.8f * info.Width, 0.2f * info.Height);

    canvas.DrawPath(path, paint);
}

float[] GetPickerArray(Picker picker)
{
    if (picker.SelectedIndex == -1)
    {
        return new float[0];
    }

    string str = (string)picker.SelectedItem;
    string[] strs = str.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
    float[] array = new float[strs.Length];

    for (int i = 0; i < strs.Length; i++)
    {
        array[i] = Convert.ToSingle(strs[i]);
    }
    return array;
}

Nas capturas de tela a seguir, a tela do iOS na extrema esquerda mostra uma linha pontilhada:

Captura de tela tripla da página Pontos e traços

No entanto, a tela do Android também deve mostrar uma linha pontilhada usando a matriz { 10, 10 }, mas em vez disso, a linha é sólida. O que aconteceu? O problema é que a tela do Android também tem uma configuração de stroke caps de Square. Isso estende todos os traços pela metade da largura do traçado, fazendo com que eles preencham as lacunas.

Para contornar esse problema ao usar um limite de traçado de ou Round, você deve diminuir os comprimentos de Square traço na matriz pelo comprimento do traçado (às vezes resultando em um comprimento de traço de 0) e aumentar os comprimentos de intervalo pelo comprimento do traçado. É assim que as três matrizes de traço finais no Picker arquivo XAML foram calculadas:

  • { 10, 10 } torna-se { 0, 20 } para uma linha pontilhada
  • { 30, 10 } torna-se { 20, 20 } para uma linha tracejada
  • { 10, 10, 30, 10 } torna-se { 0, 20, 20, 20} para uma linha pontilhada e tracejada

A tela UWP mostra essa linha pontilhada e tracejada para um limite de traçado de Round. A Round tampa de traçado geralmente dá a melhor aparência de pontos e traços em linhas grossas.

Até o momento, nenhuma menção foi feita ao segundo parâmetro do SKPathEffect.CreateDash método. Esse parâmetro é nomeado phase e refere-se a um deslocamento dentro do padrão ponto-e-traço para o início da linha. Por exemplo, se a matriz de traço for { 10, 10 } e a phase for 10, a linha começará com uma lacuna em vez de um ponto.

Uma aplicação interessante do phase parâmetro é em uma animação. A página Espiral Animada é semelhante à página Espiral de Arquimedes , exceto que a AnimatedSpiralPage classe anima o phase parâmetro usando o Xamarin.FormsDevice.Timer método:

public class AnimatedSpiralPage : ContentPage
{
    const double cycleTime = 250;       // in milliseconds

    SKCanvasView canvasView;
    Stopwatch stopwatch = new Stopwatch();
    bool pageIsActive;
    float dashPhase;

    public AnimatedSpiralPage()
    {
        Title = "Animated Spiral";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        pageIsActive = true;
        stopwatch.Start();

        Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
        {
            double t = stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime;
            dashPhase = (float)(10 * t);
            canvasView.InvalidateSurface();

            if (!pageIsActive)
            {
                stopwatch.Stop();
            }

            return pageIsActive;
        });
    }
    ···  
}

Claro, você terá que realmente executar o programa para ver a animação:

Captura de tela tripla da página Espiral Animada