Share via


SkiaSharp의 점과 대시

SkiaSharp에서 점선과 파선 그리기의 복잡성 마스터

SkiaSharp를 사용하면 단색이 아니라 점과 대시로 구성된 선을 그릴 수 있습니다.

점선

경로 효과를 사용하여 이 작업을 수행합니다. 이 작업은 속성으로 PathEffect 설정한 클래스의 SKPathEffectSKPaint인스턴스입니다. 에 정의된 SKPathEffect정적 생성 방법 중 하나를 사용하여 경로 효과를 만들거나 경로 효과를 결합할 수 있습니다. (SKPathEffectSkiaSharp에서 지원하는 여섯 가지 효과 중 하나이며, 다른 효과는 SkiaSharp 효과 섹션에 설명되어 있습니다.)

점선 또는 파선선을 그리려면 정적 메서드를 SKPathEffect.CreateDash 사용합니다. 두 인수가 있습니다. 첫 번째는 점과 대시의 float 길이와 그 사이의 공백 길이를 나타내는 값 배열입니다. 이 배열에는 짝수의 요소가 있어야 하며 두 개 이상의 요소가 있어야 합니다. (배열에는 요소가 0일 수 있지만 실선이 생성됩니다.) 두 개의 요소가 있는 경우 첫 번째는 점 또는 대시의 길이이고, 두 번째는 다음 점 또는 대시 앞의 간격 길이입니다. 두 개 이상의 요소가 있는 경우 대시 길이, 간격 길이, 대시 길이, 간격 길이 등 순서대로 표시됩니다.

일반적으로 대시와 간격 길이를 스트로크 너비의 배수로 만들려고 합니다. 예를 들어 스트로크 너비가 10픽셀인 경우 배열 { 10, 10 }은 점과 간격의 길이가 스트로크 두께와 같은 점선을 그립니다.

그러나 개체의 설정은 StrokeCapSKPaint 이러한 점과 대시에도 영향을 줍니다. 곧 볼 수 있듯이 이 배열의 요소에 영향을 미칩니다.

점선 및 대시 페이지에 점선과 파선이 표시됩니다. DotsAndDashesPage.xaml 파일은 스트로크 캡을 선택할 수 있도록 하는 뷰와 대시 배열을 선택하는 두 Picker 번째 뷰를 인스턴스화합니다.

<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>

처음 세 항목은 dashArrayPicker 스트로크 너비가 10픽셀이라고 가정합니다. { 10, 10 } 배열은 점선, { 30, 10 }는 파선, { 10, 10, 30, 10 }은 점 및 대시 선에 대한 것입니다. (나머지 세 가지는 곧 논의될 예정입니다.)

코드 숨김 파일에는 DotsAndDashesPage 이벤트 처리기와 보기에 액세스하기 위한 몇 가지 도우미 루틴이 Picker 포함되어 PaintSurface 있습니다.

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;
}

다음 스크린샷에서 맨 왼쪽의 iOS 화면에 점선이 표시됩니다.

점 및 대시 페이지의 삼중 스크린샷

그러나 Android 화면은 배열 { 10, 10 }을(를) 사용하여 점선도 표시해야 하지만 대신 선은 단색입니다. 무슨 일이 일어났나요? 문제는 Android 화면에 스트로크 대문자 설정 Square도 있다는 것입니다. 이렇게 하면 모든 대시가 스트로크 너비의 절반으로 확장되어 간격을 채웁니다.

또는 스트로크 캡을 사용할 때 이 문제를 해결하려면 배열의 SquareRound대시 길이를 스트로크 길이로 줄이고(때로는 대시 길이가 0인 경우) 간격 길이를 스트로크 길이로 늘려야 합니다. XAML 파일의 마지막 세 개의 대시 배열을 Picker 계산한 방법은 다음과 같습니다.

  • { 10, 10 }이(가) 점선의 경우 { 0, 20 }이 됩니다.
  • { 30, 10 }이(가) 파선의 경우 { 20, 20 }이 됩니다.
  • { 점선과 파선의 경우 { 10, 10, 30, 10 } 이 {0, 20, 20, 20}이 됩니다.

UWP 화면에는 스트로크 캡 Round의 점선과 파선이 표시됩니다. 스트로크 캡은 Round 종종 두꺼운 선에서 점과 대시의 최상의 모양을 제공합니다.

지금까지 메서드에 대한 두 번째 매개 변수 SKPathEffect.CreateDash 로 만들어진 멘션 없습니다. 이 매개 변수의 이름은 phase 선의 시작 부분에 대한 점 및 대시 패턴 내의 오프셋을 나타냅니다. 예를 들어 대시 배열이 { 10, 10 }이고 phase 10이면 선이 점이 아닌 간격으로 시작합니다.

매개 변수의 phase 흥미로운 응용 프로그램 중 하나는 애니메이션에 있습니다. 애니메이션 나선형 페이지는 클래스가 메서드를 사용하여 Xamarin.FormsDevice.Timer 매개 변수에 애니메이션 효과를 주는 것을 AnimatedSpiralPage 제외하고 Archimedean 나선형 페이지와 유사합니다phase.

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;
        });
    }
    ···  
}

물론 애니메이션을 보려면 프로그램을 실제로 실행해야 합니다.

애니메이션 나선형 페이지의 삼중 스크린샷