SkiaSharp 선형 그라데이션
이 클래스는 SKPaint
선을 획하거나 단색으로 영역을 채우는 데 사용되는 속성을 정의 Color
합니다. 또는 선이나 그라데이션으로 영역을 채울 수 있습니다. 이 그라데이션은 색의 점진적 혼합입니다.
가장 기본적인 그라데이션 형식은 선형 그라데이션입니다. 색의 혼합은 한 지점에서 다른 지점으로 선(그라데이션 선이라고 함)에서 발생합니다. 그라데이션 선에 수직인 선의 색은 동일합니다. 두 정적 SKShader.CreateLinearGradient
메서드 중 하나를 사용하여 선형 그라데이션을 만듭니다. 두 오버로드 간의 차이점은 하나는 행렬 변환을 포함하고 다른 하나는 그렇지 않다는 것입니다.
이러한 메서드는 .의 속성SKPaint
으로 설정한 형식 SKShader
의 개체를 Shader
반환합니다. 속성이 Shader
null이 아닌 경우 속성을 재정의 Color
합니다. 스트로크된 모든 선 또는 이 SKPaint
개체를 사용하여 채워진 영역은 단색이 아닌 그라데이션을 기반으로 합니다.
참고 항목
Shader
호출에 개체를 포함하면 속성이 SKPaint
DrawBitmap
무시됩니다. 속성을 사용하여 Color
SKPaint
비트맵을 표시하기 위한 투명도 수준을 설정할 수 있지만(SkiaSharp 비트맵 표시 문서에 설명된 대로) 그라데이션 투명도가 있는 비트맵을 표시하는 데는 이 속성을 사용할 Shader
수 없습니다. 그라데이션 투명도를 사용하여 비트맵을 표시하는 데 사용할 수 있는 다른 기법은 SkiaSharp 원형 그라데이션 및 SkiaSharp 작성 및 혼합 모드 문서에 설명되어 있습니다.
모서리-모서리 그라데이션
선형 그라데이션이 사각형의 한 모서리에서 다른 모서리로 확장되는 경우가 많습니다. 시작점이 사각형의 왼쪽 위 모서리인 경우 그라데이션은 다음을 확장할 수 있습니다.
- 왼쪽 아래 모서리에 세로로
- 오른쪽 위 모서리에 가로로
- 오른쪽 아래 모서리에 대각선으로
대각선 선형 그라데이션은 샘플의 SkiaSharp 셰이더 및 기타 효과 섹션의 첫 번째 페이지에 설명되어 있습니다 . 코너-모퉁이 그라데이션 페이지는 생성자에 생성자를 만듭니다SKCanvasView
. PaintSurface
처리기는 문에 개체를 SKPaint
using
만든 다음 캔버스 가운데에 있는 300픽셀 사각형을 정의합니다.
public class CornerToCornerGradientPage : ContentPage
{
···
public CornerToCornerGradientPage ()
{
Title = "Corner-to-Corner Gradient";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
···
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
// Create 300-pixel square centered rectangle
float x = (info.Width - 300) / 2;
float y = (info.Height - 300) / 2;
SKRect rect = new SKRect(x, y, x + 300, y + 300);
// Create linear gradient from upper-left to lower-right
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(rect.Left, rect.Top),
new SKPoint(rect.Right, rect.Bottom),
new SKColor[] { SKColors.Red, SKColors.Blue },
new float[] { 0, 1 },
SKShaderTileMode.Repeat);
// Draw the gradient on the rectangle
canvas.DrawRect(rect, paint);
···
}
}
}
속성 SKPaint
에 Shader
정적 SKShader.CreateLinearGradient
메서드의 SKShader
반환 값이 할당됩니다. 5개의 인수는 다음과 같습니다.
- 그라데이션의 시작점으로, 사각형의 왼쪽 위 모서리로 설정합니다.
- 그라데이션의 끝점입니다. 여기서 사각형의 오른쪽 아래 모서리로 설정합니다.
- 그라데이션에 영향을 주는 두 개 이상의 색 배열
- 그라데이션 선 내의 색의 상대 위치를 나타내는 값의 배열
float
입니다. - 그라데이션이
SKShaderTileMode
그라데이션 선의 끝을 넘어 동작하는 방식을 나타내는 열거형의 멤버입니다.
그라데이션 개체를 만든 DrawRect
후 메서드는 셰이더를 포함하는 개체를 사용하여 SKPaint
300픽셀 사각형 사각형을 그립니다. 여기서는 iOS, Android 및 UWP(유니버설 Windows 플랫폼)에서 실행됩니다.
그라데이션 선은 처음 두 인수로 지정된 두 점으로 정의됩니다. 이러한 점은 그라데이션과 함께 표시되는 그래픽 개체가 아니라 캔버스를 기준으로 합니다. 그라데이션 선을 따라 색이 왼쪽 상단의 빨간색에서 오른쪽 아래의 파란색으로 점차 전환됩니다. 그라데이션 선에 수직인 모든 선은 상수 색을 가집니다.
네 번째 인수로 지정된 값 배열 float
에는 색 배열과 일대일 대응이 있습니다. 값은 해당 색이 발생하는 그라데이션 선의 상대 위치를 나타냅니다. 여기서 0은 Red
그라데이션 선의 시작 부분에 발생하는 것을 의미하고, 1은 줄의 끝에서 발생한다는 Blue
것을 의미합니다. 숫자는 오름차순이어야 하며 0에서 1까지의 범위에 있어야 합니다. 해당 범위에 없는 경우 해당 범위에 있도록 조정됩니다.
배열의 두 값은 0과 1이 아닌 값으로 설정할 수 있습니다. 다음을 실행해보세요.
new float[] { 0.25f, 0.75f }
이제 그라데이션 라인의 전체 1 분기는 순수한 빨간색이며, 마지막 분기는 순수한 파란색입니다. 빨간색과 파란색의 혼합은 그라데이션 선의 중앙 절반으로 제한됩니다.
일반적으로 이러한 위치 값의 간격을 0에서 1로 균등하게 지정할 수 있습니다. 이 경우 네 번째 인수CreateLinearGradient
로 간단히 제공할 null
수 있습니다.
이 그라데이션은 300픽셀 정사각형 사각형의 두 모서리 사이에 정의되어 있지만 해당 사각형을 채우는 것으로 제한되지는 않습니다. 코너-모서리 그라데이션 페이지에는 페이지의 탭 또는 마우스 클릭에 응답하는 몇 가지 추가 코드가 포함되어 있습니다. drawBackground
필드는 탭할 때마다 전환 true
됩니다false
. 값이 true
PaintSurface
면 처리기는 동일한 SKPaint
개체를 사용하여 전체 캔버스를 채운 다음 작은 사각형을 나타내는 검은색 사각형을 그립니다.
public class CornerToCornerGradientPage : ContentPage
{
bool drawBackground;
public CornerToCornerGradientPage ()
{
···
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender, args) =>
{
drawBackground ^= true;
canvasView.InvalidateSurface();
};
canvasView.GestureRecognizers.Add(tap);
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
using (SKPaint paint = new SKPaint())
{
···
if (drawBackground)
{
// Draw the gradient on the whole canvas
canvas.DrawRect(info.Rect, paint);
// Outline the smaller rectangle
paint.Shader = null;
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Black;
canvas.DrawRect(rect, paint);
}
}
}
}
화면을 탭한 후 표시되는 내용은 다음과 같습니다.
그라데이션은 그라데이션 선을 정의하는 점 이외의 동일한 패턴으로 반복됩니다. 이 반복은 마지막 인수 CreateLinearGradient
가 .이므로 SKShaderTileMode.Repeat
발생합니다. (곧 다른 옵션이 표시됩니다.)
또한 그라데이션 선을 지정하는 데 사용하는 점이 고유하지 않습니다. 그라데이션 선에 수직인 선의 색은 같으므로 동일한 효과에 대해 지정할 수 있는 그라데이션 선 수가 무한합니다. 예를 들어 사각형을 가로 그라데이션으로 채울 때 왼쪽 위와 오른쪽 위 모서리, 왼쪽 아래 및 오른쪽 아래 모서리 또는 해당 선과 짝수 및 병렬인 두 점을 지정할 수 있습니다.
대화형 실험
대화형 선형 그라데이션 페이지를 사용하여 선형 그라데이션을 대화형으로 실험할 수 있습니다. 이 페이지에서는 세 가지 방법으로 호를 그리는 문서에 소개된 클래스를 사용합니다InteractivePage
. InteractivePage
이벤트를 처리 TouchEffect
하여 손가락이나 마우스로 이동할 수 있는 개체 컬렉션을 TouchPoint
기본.
XAML 파일은 부모에 연결 TouchEffect
하며 열거형의 SKCanvasView
세 멤버 SKShaderTileMode
중 하나를 선택할 수 있는 파일도 포함합니다Picker
.
<local:InteractivePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SkiaSharpFormsDemos"
xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
xmlns:tt="clr-namespace:TouchTracking"
x:Class="SkiaSharpFormsDemos.Effects.InteractiveLinearGradientPage"
Title="Interactive Linear Gradient">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid BackgroundColor="White"
Grid.Row="0">
<skiaforms:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True"
TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>
<Picker x:Name="tileModePicker"
Grid.Row="1"
Title="Shader Tile Mode"
Margin="10"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type skia:SKShaderTileMode}">
<x:Static Member="skia:SKShaderTileMode.Clamp" />
<x:Static Member="skia:SKShaderTileMode.Repeat" />
<x:Static Member="skia:SKShaderTileMode.Mirror" />
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
0
</Picker.SelectedIndex>
</Picker>
</Grid>
</local:InteractivePage>
코드 숨김 파일의 생성자는 선형 그라데이션의 시작점과 끝점에 대해 두 개의 TouchPoint
개체를 만듭니다. PaintSurface
처리기는 세 가지 색(빨간색에서 녹색에서 파랑으로 그라데이션)의 배열을 정의하고 다음에서 전류 SKShaderTileMode
를 Picker
가져옵니다.
public partial class InteractiveLinearGradientPage : InteractivePage
{
public InteractiveLinearGradientPage ()
{
InitializeComponent ();
touchPoints = new TouchPoint[2];
for (int i = 0; i < 2; i++)
{
touchPoints[i] = new TouchPoint
{
Center = new SKPoint(100 + i * 200, 100 + i * 200)
};
}
InitializeComponent();
baseCanvasView = canvasView;
}
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
canvasView.InvalidateSurface();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKColor[] colors = { SKColors.Red, SKColors.Green, SKColors.Blue };
SKShaderTileMode tileMode =
(SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
0 : tileModePicker.SelectedItem);
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateLinearGradient(touchPoints[0].Center,
touchPoints[1].Center,
colors,
null,
tileMode);
canvas.DrawRect(info.Rect, paint);
}
···
}
}
PaintSurface
처리기는 모든 정보에서 개체를 만들고 SKShader
전체 캔버스에 색을 지정하는 데 사용합니다. 값 배열 float
이 .로 설정됩니다 null
. 그렇지 않으면 세 색의 간격을 동일하게 지정하려면 해당 매개 변수를 값이 0, 0.5 및 1인 배열로 설정합니다.
처리기의 PaintSurface
대부분은 여러 개체를 표시하는 데 전념합니다. 즉, 터치 포인트는 윤곽선으로, 그라데이션 선은 터치 포인트의 그라데이션 선에 수직으로 표시됩니다.
public partial class InteractiveLinearGradientPage : InteractivePage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
// Display the touch points here rather than by TouchPoint
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Black;
paint.StrokeWidth = 3;
foreach (TouchPoint touchPoint in touchPoints)
{
canvas.DrawCircle(touchPoint.Center, touchPoint.Radius, paint);
}
// Draw gradient line connecting touchpoints
canvas.DrawLine(touchPoints[0].Center, touchPoints[1].Center, paint);
// Draw lines perpendicular to the gradient line
SKPoint vector = touchPoints[1].Center - touchPoints[0].Center;
float length = (float)Math.Sqrt(Math.Pow(vector.X, 2) +
Math.Pow(vector.Y, 2));
vector.X /= length;
vector.Y /= length;
SKPoint rotate90 = new SKPoint(-vector.Y, vector.X);
rotate90.X *= 200;
rotate90.Y *= 200;
canvas.DrawLine(touchPoints[0].Center,
touchPoints[0].Center + rotate90,
paint);
canvas.DrawLine(touchPoints[0].Center,
touchPoints[0].Center - rotate90,
paint);
canvas.DrawLine(touchPoints[1].Center,
touchPoints[1].Center + rotate90,
paint);
canvas.DrawLine(touchPoints[1].Center,
touchPoints[1].Center - rotate90,
paint);
}
}
}
두 터치포인트를 연결하는 그라데이션 선은 그리기 쉽지만 수직 선에는 좀 더 많은 작업이 필요합니다. 그라데이션 선은 벡터로 변환되고, 하나의 단위 길이로 정규화된 다음, 90도 회전됩니다. 그런 다음, 해당 벡터의 길이는 200픽셀로 지정됩니다. 터치 포인트에서 수직으로 확장되는 4개의 선을 그라데이션 선으로 그리는 데 사용됩니다.
수직 선은 그라데이션의 시작과 끝과 일치합니다. 이러한 줄을 벗어나는 작업은 열거형의 설정에 SKShaderTileMode
따라 달라집니다.
세 개의 스크린샷은 세 가지 값 SKShaderTileMode
의 결과를 보여 줍니다. iOS 스크린샷은 그라데이션 테두리의 색만 확장하는 것을 보여 SKShaderTileMode.Clamp
줍니다. Android 스크린샷의 옵션은 SKShaderTileMode.Repeat
그라데이션 패턴이 반복되는 방식을 보여 줍니다. SKShaderTileMode.Mirror
UWP 스크린샷의 옵션도 패턴을 반복하지만 패턴은 매번 반전되어 색 불연속성이 없습니다.
그라데이션의 그라데이션
클래스는 SKShader
public 속성 또는 메서드를 제외하고 Dispose
정의하지 않습니다. SKShader
따라서 정적 메서드에서 만든 개체는 변경할 수 없습니다. 서로 다른 두 개체에 동일한 그라데이션을 사용하더라도 그라데이션을 약간 변경할 수 있습니다. 이렇게 하려면 새 SKShader
개체를 만들어야 합니다.
그라데이션 텍스트 페이지에는 유사한 그라데이션으로 색이 지정된 텍스트와 브랙그라운드가 표시됩니다.
그라데이션의 유일한 차이점은 시작점과 끝점입니다. 텍스트를 표시하는 데 사용되는 그라데이션은 텍스트의 경계 사각형 모서리에 있는 두 점을 기반으로 합니다. 배경의 경우 두 점은 전체 캔버스를 기반으로합니다. 코드는 다음과 같습니다.
public class GradientTextPage : ContentPage
{
const string TEXT = "GRADIENT";
public GradientTextPage ()
{
Title = "Gradient Text";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
// Create gradient for background
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint(info.Width, info.Height),
new SKColor[] { new SKColor(0x40, 0x40, 0x40),
new SKColor(0xC0, 0xC0, 0xC0) },
null,
SKShaderTileMode.Clamp);
// Draw background
canvas.DrawRect(info.Rect, paint);
// Set TextSize to fill 90% of width
paint.TextSize = 100;
float width = paint.MeasureText(TEXT);
float scale = 0.9f * info.Width / width;
paint.TextSize *= scale;
// Get text bounds
SKRect textBounds = new SKRect();
paint.MeasureText(TEXT, ref textBounds);
// Calculate offsets to center the text on the screen
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
// Shift textBounds by that amount
textBounds.Offset(xText, yText);
// Create gradient for text
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(textBounds.Left, textBounds.Top),
new SKPoint(textBounds.Right, textBounds.Bottom),
new SKColor[] { new SKColor(0x40, 0x40, 0x40),
new SKColor(0xC0, 0xC0, 0xC0) },
null,
SKShaderTileMode.Clamp);
// Draw text
canvas.DrawText(TEXT, xText, yText, paint);
}
}
}
Shader
개체의 SKPaint
속성은 배경을 덮는 그라데이션을 표시하기 위해 먼저 설정됩니다. 그라데이션 점은 캔버스의 왼쪽 위와 오른쪽 아래 모서리로 설정됩니다.
이 코드는 텍스트가 TextSize
캔버스 너비의 SKPaint
90%에 표시되도록 개체의 속성을 설정합니다. 텍스트 범위는 계산 xText
에 사용되며 yText
값을 메서드에 DrawText
전달하여 텍스트를 가운데에 배치합니다.
그러나 두 번째 CreateLinearGradient
호출의 그라데이션 점은 캔버스가 표시될 때 캔버스를 기준으로 텍스트의 왼쪽 위와 오른쪽 아래 모서리를 참조해야 합니다. 이 작업은 직사각형을 textBounds
동일한 xText
값으로 yText
이동하여 수행합니다.
textBounds.Offset(xText, yText);
이제 사각형의 왼쪽 위와 오른쪽 아래 모서리를 사용하여 그라데이션의 시작점과 끝점을 설정할 수 있습니다.
그라데이션 애니메이션
그라데이션에 애니메이션 효과를 적용하는 방법에는 여러 가지가 있습니다. 한 가지 방법은 시작점과 끝점에 애니메이션을 적용하는 것입니다. 그라데이션 애니메이션 페이지는 캔버스 가운데에 있는 원에서 두 점을 이동합니다. 이 원의 반경은 캔버스의 너비 또는 높이의 절반이며, 그 중에서 더 작습니다. 시작점과 끝점은 이 원에서 서로 반대이며, 그라데이션은 타일 모드를 사용하여 Mirror
흰색에서 검은색으로 바꿉니다.
생성자는 .를 SKCanvasView
만듭니다. 및 OnDisappearing
메서드는 OnAppearing
애니메이션 논리를 처리합니다.
public class GradientAnimationPage : ContentPage
{
SKCanvasView canvasView;
bool isAnimating;
double angle;
Stopwatch stopwatch = new Stopwatch();
public GradientAnimationPage()
{
Title = "Gradient Animation";
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
protected override void OnAppearing()
{
base.OnAppearing();
isAnimating = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
stopwatch.Stop();
isAnimating = false;
}
bool OnTimerTick()
{
const int duration = 3000;
angle = 2 * Math.PI * (stopwatch.ElapsedMilliseconds % duration) / duration;
canvasView.InvalidateSurface();
return isAnimating;
}
···
}
이 메서드는 OnTimerTick
3초마다 0에서 2π까지 애니메이션 효과를 주는 값을 계산합니다 angle
.
다음은 두 그라데이션 점을 계산하는 한 가지 방법입니다. 명명된 SKPoint
vector
값은 캔버스의 중심에서 원 반경의 지점까지 확장되도록 계산됩니다. 이 벡터의 방향은 각도의 사인 및 코사인 값을 기반으로 합니다. 그런 다음 두 개의 반대 그라데이션 점이 계산됩니다. 한 지점은 중심점에서 해당 벡터를 빼서 계산되고 다른 점은 중심점에 벡터를 추가하여 계산됩니다.
public class GradientAnimationPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
int radius = Math.Min(info.Width, info.Height) / 2;
SKPoint vector = new SKPoint((float)(radius * Math.Cos(angle)),
(float)(radius * Math.Sin(angle)));
paint.Shader = SKShader.CreateLinearGradient(
center - vector,
center + vector,
new SKColor[] { SKColors.White, SKColors.Black },
null,
SKShaderTileMode.Mirror);
canvas.DrawRect(info.Rect, paint);
}
}
}
다소 다른 접근 방식에는 코드가 더 적게 필요합니다. 이 방법은 행렬 변환과 SKShader.CreateLinearGradient
함께 오버로드 메서드를 마지막 인수로 사용합니다. 이 방법은 샘플의 버전입니다.
public class GradientAnimationPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
info.Width < info.Height ? new SKPoint(info.Width, 0) :
new SKPoint(0, info.Height),
new SKColor[] { SKColors.White, SKColors.Black },
new float[] { 0, 1 },
SKShaderTileMode.Mirror,
SKMatrix.MakeRotation((float)angle, info.Rect.MidX, info.Rect.MidY));
canvas.DrawRect(info.Rect, paint);
}
}
}
캔버스의 너비가 높이보다 작으면 두 그라데이션 점이 (0, 0) 및 (info.Width
, 0)로 설정됩니다. 마지막 인수 CreateLinearGradient
로 전달된 회전 변환은 화면 중앙을 중심으로 두 점을 효과적으로 회전합니다.
각도가 0이면 회전이 없고 두 그라데이션 점은 캔버스의 왼쪽 위와 오른쪽 위 모서리입니다. 이러한 점은 이전 CreateLinearGradient
호출에 표시된 것과 동일한 그라데이션 포인트가 아닙니다. 그러나 이러한 점들은 캔버스의 중심을 양분하는 가로 그라데이션 선과 평행 하며 동일한 그라데이션을 생성합니다.
레인보우 그라데이션
레인보우 그라데이션 페이지는 캔버스의 왼쪽 위 모서리에서 오른쪽 아래 모서리까지 무지개를 그립니다. 그러나이 무지개 그라데이션은 실제 무지개처럼되지 않습니다. 곡선이 아닌 직선이지만 0에서 360까지의 색조 값을 순환하여 결정되는 8개의 HSL(색조 채도-광도) 색을 기반으로 합니다.
SKColor[] colors = new SKColor[8];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
}
이 코드는 아래에 표시된 처리기의 PaintSurface
일부입니다. 처리기는 캔버스의 왼쪽 위 모서리에서 오른쪽 아래 모서리까지 확장하는 6면 다각형을 정의하는 경로를 만드는 것으로 시작합니다.
public class RainbowGradientPage : ContentPage
{
public RainbowGradientPage ()
{
Title = "Rainbow Gradient";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPath path = new SKPath())
{
float rainbowWidth = Math.Min(info.Width, info.Height) / 2f;
// Create path from upper-left to lower-right corner
path.MoveTo(0, 0);
path.LineTo(rainbowWidth / 2, 0);
path.LineTo(info.Width, info.Height - rainbowWidth / 2);
path.LineTo(info.Width, info.Height);
path.LineTo(info.Width - rainbowWidth / 2, info.Height);
path.LineTo(0, rainbowWidth / 2);
path.Close();
using (SKPaint paint = new SKPaint())
{
SKColor[] colors = new SKColor[8];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
}
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, rainbowWidth / 2),
new SKPoint(rainbowWidth / 2, 0),
colors,
null,
SKShaderTileMode.Repeat);
canvas.DrawPath(path, paint);
}
}
}
}
메서드의 CreateLinearGradient
두 그라데이션 점은 이 경로를 정의하는 두 점을 기반으로 합니다. 두 점 모두 왼쪽 위 모서리에 가깝습니다. 첫 번째는 캔버스의 위쪽 가장자리에 있고 두 번째는 캔버스의 왼쪽 가장자리에 있습니다. 결과:
이것은 흥미로운 이미지이지만 의도는 아닙니다. 문제는 선형 그라데이션을 만들 때 상수 색의 선이 그라데이션 선에 수직적이라는 것입니다. 그라데이션 선은 그림이 위쪽과 왼쪽에 닿는 점을 기반으로 하며, 이 선은 일반적으로 오른쪽 아래 모서리까지 확장되는 그림의 가장자리에 수직이 아닙니다. 이 방법은 캔버스가 정사각형인 경우에만 작동합니다.
적절한 무지개 그라데이션을 만들려면 그라데이션 선이 무지개 가장자리에 수직이어야 합니다. 이는 더 관련된 계산입니다. 그림의 긴 면과 평행한 벡터를 정의해야 합니다. 벡터는 90도 회전되므로 해당 쪽에 수직으로 회전합니다. 그런 다음 을 곱 rainbowWidth
하여 그림의 너비로 길어집니다. 두 그라데이션 점은 그림 측면의 점과 해당 점 및 벡터를 기준으로 계산됩니다. 샘플의 레인보우 그라데이션 페이지에 표시되는 코드는 다음과 같습니다.
public class RainbowGradientPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
using (SKPath path = new SKPath())
{
···
using (SKPaint paint = new SKPaint())
{
···
// Vector on lower-left edge, from top to bottom
SKPoint edgeVector = new SKPoint(info.Width - rainbowWidth / 2, info.Height) -
new SKPoint(0, rainbowWidth / 2);
// Rotate 90 degrees counter-clockwise:
SKPoint gradientVector = new SKPoint(edgeVector.Y, -edgeVector.X);
// Normalize
float length = (float)Math.Sqrt(Math.Pow(gradientVector.X, 2) +
Math.Pow(gradientVector.Y, 2));
gradientVector.X /= length;
gradientVector.Y /= length;
// Make it the width of the rainbow
gradientVector.X *= rainbowWidth;
gradientVector.Y *= rainbowWidth;
// Calculate the two points
SKPoint point1 = new SKPoint(0, rainbowWidth / 2);
SKPoint point2 = point1 + gradientVector;
paint.Shader = SKShader.CreateLinearGradient(point1,
point2,
colors,
null,
SKShaderTileMode.Repeat);
canvas.DrawPath(path, paint);
}
}
}
}
이제 무지개 색이 그림에 맞춰집니다.
무한대 색
무지개 그라데이션은 무한대 색 페이지에서도 사용됩니다. 이 페이지는 세 가지 유형의 베지어 곡선 문서에 설명된 경로 개체를 사용하여 무한대 기호를 그립니다. 그런 다음 이미지 전체에 지속적으로 스윕되는 애니메이션 레인보우 그라데이션으로 이미지가 색이 지정됩니다.
생성자는 무한대 기호를 설명하는 개체를 만듭니다 SKPath
. 경로를 만든 후 생성자는 경로의 사각형 범위를 가져올 수도 있습니다. 그런 다음 , 라는 gradientCycleLength
값을 계산합니다. 그라데이션이 사각형의 왼쪽 위와 오른쪽 아래 모서리를 pathBounds
기반으로 하는 경우 이 gradientCycleLength
값은 그라데이션 패턴의 총 가로 너비입니다.
public class InfinityColorsPage : ContentPage
{
···
SKCanvasView canvasView;
// Path information
SKPath infinityPath;
SKRect pathBounds;
float gradientCycleLength;
// Gradient information
SKColor[] colors = new SKColor[8];
···
public InfinityColorsPage ()
{
Title = "Infinity Colors";
// Create path for infinity sign
infinityPath = new SKPath();
infinityPath.MoveTo(0, 0); // Center
infinityPath.CubicTo( 50, -50, 95, -100, 150, -100); // To top of right loop
infinityPath.CubicTo( 205, -100, 250, -55, 250, 0); // To far right of right loop
infinityPath.CubicTo( 250, 55, 205, 100, 150, 100); // To bottom of right loop
infinityPath.CubicTo( 95, 100, 50, 50, 0, 0); // Back to center
infinityPath.CubicTo( -50, -50, -95, -100, -150, -100); // To top of left loop
infinityPath.CubicTo(-205, -100, -250, -55, -250, 0); // To far left of left loop
infinityPath.CubicTo(-250, 55, -205, 100, -150, 100); // To bottom of left loop
infinityPath.CubicTo( -95, 100, - 50, 50, 0, 0); // Back to center
infinityPath.Close();
// Calculate path information
pathBounds = infinityPath.Bounds;
gradientCycleLength = pathBounds.Width +
pathBounds.Height * pathBounds.Height / pathBounds.Width;
// Create SKColor array for gradient
for (int i = 0; i < colors.Length; i++)
{
colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
}
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
···
}
또한 생성자는 무지개 및 개체에 대한 배열을 SKCanvasView
만듭니다colors
.
재정의 및 OnDisappearing
메서드는 OnAppearing
애니메이션에 대한 오버헤드를 수행합니다. 이 메서드는 OnTimerTick
필드에 0에서 2초마다 애니메이션 효과를 gradientCycleLength
봅니 offset
다.
public class InfinityColorsPage : ContentPage
{
···
// For animation
bool isAnimating;
float offset;
Stopwatch stopwatch = new Stopwatch();
···
protected override void OnAppearing()
{
base.OnAppearing();
isAnimating = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
stopwatch.Stop();
isAnimating = false;
}
bool OnTimerTick()
{
const int duration = 2; // seconds
double progress = stopwatch.Elapsed.TotalSeconds % duration / duration;
offset = (float)(gradientCycleLength * progress);
canvasView.InvalidateSurface();
return isAnimating;
}
···
}
마지막으로 처리기는 PaintSurface
무한대 기호를 렌더링합니다. 경로는 중심점(0, 0) Translate
을 둘러싼 음수 및 양수 좌표를 포함하므로 캔버스의 변환을 사용하여 가운데로 이동합니다. 변환 변환 뒤에는 캔버스 너비와 높이의 95% 이내를 유지하면서 무한대 기호를 최대한 크게 만드는 배율 인수를 적용하는 변환이 뒤따 Scale
릅니다.
STROKE_WIDTH
상수는 경로 경계 사각형의 너비와 높이에 추가됩니다. 경로는 이 너비의 선으로 스트로크되므로 렌더링된 무한대 크기의 크기가 네 면 모두에 있는 너비의 절반으로 증가합니다.
public class InfinityColorsPage : ContentPage
{
const int STROKE_WIDTH = 50;
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Set transforms to shift path to center and scale to canvas size
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.Scale(0.95f *
Math.Min(info.Width / (pathBounds.Width + STROKE_WIDTH),
info.Height / (pathBounds.Height + STROKE_WIDTH)));
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.StrokeWidth = STROKE_WIDTH;
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(pathBounds.Left, pathBounds.Top),
new SKPoint(pathBounds.Right, pathBounds.Bottom),
colors,
null,
SKShaderTileMode.Repeat,
SKMatrix.MakeTranslation(offset, 0));
canvas.DrawPath(infinityPath, paint);
}
}
}
의 처음 두 인수로 전달된 점을 확인합니다 SKShader.CreateLinearGradient
. 이러한 점은 원래 경로 경계 사각형을 기반으로 합니다. 첫 번째 점은 (-250, –100)이고 두 번째는 (250, 100)입니다. SkiaSharp의 내부 요소에는 현재 캔버스 변환이 적용되므로 표시된 무한대 기호에 올바르게 맞춥니다.
마지막 인수 CreateLinearGradient
가 없으면 무한대 기호의 왼쪽 위에서 오른쪽 아래까지 확장되는 무지개 그라데이션이 표시됩니다. (실제로 그라데이션은 왼쪽 위 모서리에서 경계 사각형의 오른쪽 아래 모서리까지 확장됩니다. 렌더링된 무한대 기호는 경계 사각형보다 모든 면의 값의 STROKE_WIDTH
절반보다 큽니다. 그라데이션은 시작과 끝 모두에서 빨간색이고 그라데이션이 생성 SKShaderTileMode.Repeat
되므로 차이가 눈에 띄지 않습니다.)
마지막 인수를 사용하면 CreateLinearGradient
그라데이션 패턴이 이미지 전체에서 계속 스윕됩니다.
투명도 및 그라데이션
그라데이션에 영향을 주는 색은 투명도를 통합할 수 있습니다. 그라데이션은 한 색에서 다른 색으로 페이드되는 그라데이션 대신 색에서 투명으로 페이드할 수 있습니다.
몇 가지 흥미로운 효과에 대 한이 기술을 사용할 수 있습니다. 클래식 예제 중 하나는 리플렉션이 있는 그래픽 개체를 보여 줍니다.
거꾸로 된 텍스트는 위쪽에 50% 투명하고 아래쪽에 완전히 투명하게 그라데이션으로 색이 지정됩니다. 이러한 수준의 투명도는 0x80 및 0의 알파 값과 연결됩니다.
PaintSurface
리플렉션 그라데이션 페이지의 처리기는 텍스트 크기를 캔버스 너비의 90%로 조정합니다. 그런 다음, 텍스트를 가로 가운데에 배치하지만 페이지의 세로 가운데에 해당하는 기준선에 배치하는 값을 계산 xText
합니다 yText
.
public class ReflectionGradientPage : ContentPage
{
const string TEXT = "Reflection";
public ReflectionGradientPage ()
{
Title = "Reflection Gradient";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
// Set text color to blue
paint.Color = SKColors.Blue;
// Set text size to fill 90% of width
paint.TextSize = 100;
float width = paint.MeasureText(TEXT);
float scale = 0.9f * info.Width / width;
paint.TextSize *= scale;
// Get text bounds
SKRect textBounds = new SKRect();
paint.MeasureText(TEXT, ref textBounds);
// Calculate offsets to position text above center
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2;
// Draw unreflected text
canvas.DrawText(TEXT, xText, yText, paint);
// Shift textBounds to match displayed text
textBounds.Offset(xText, yText);
// Use those offsets to create a gradient for the reflected text
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, textBounds.Top),
new SKPoint(0, textBounds.Bottom),
new SKColor[] { paint.Color.WithAlpha(0),
paint.Color.WithAlpha(0x80) },
null,
SKShaderTileMode.Clamp);
// Scale the canvas to flip upside-down around the vertical center
canvas.Scale(1, -1, 0, yText);
// Draw reflected text
canvas.DrawText(TEXT, xText, yText, paint);
}
}
}
이러한 xText
값과 값은 처리기 맨 아래에 PaintSurface
있는 호출에 DrawText
반영된 텍스트를 표시하는 데 사용되는 값과 yText
동일합니다. 그러나 해당 코드 바로 앞에는 메서드에 대한 호출이 Scale
SKCanvas
표시됩니다. 이 Scale
메서드는 가로로 1(아무 작업도 수행하지 않음)으로 확장되지만 세로로 –1로 조정되어 모든 항목을 거꾸로 효과적으로 대칭 이동합니다. 회전 중심은 캔버스의 세로 중심인 점(0 yText
)으로 설정되며 yText
, 원래는 2로 나눈 값으로 info.Height
계산됩니다.
Skia는 캔버스 변환 전에 그라데이션을 사용하여 그래픽 개체에 색을 지정합니다. 반사되지 않은 텍스트가 그려지면 textBounds
표시된 텍스트에 해당하도록 사각형이 이동합니다.
textBounds.Offset(xText, yText);
호출은 CreateLinearGradient
해당 사각형의 위쪽에서 아래쪽으로 그라데이션을 정의합니다. 그라데이션은 완전히 투명한 파란색(paint.Color.WithAlpha(0)
)에서 50% 투명 파랑(paint.Color.WithAlpha(0x80)
)입니다. 캔버스 변환은 텍스트를 거꾸로 대칭 이동하므로 50% 투명한 파란색이 기준선에서 시작되고 텍스트 맨 위에서 투명해집니다.