회전 변환
SkiaSharp 회전 변환을 사용하여 가능한 효과 및 애니메이션 살펴보기
회전 변환을 사용하면 SkiaSharp 그래픽 개체가 가로 및 세로 축과의 맞춤 제약 조건에서 벗어나게 됩니다.
지점(0, 0)을 중심으로 그래픽 개체를 회전하는 경우 SkiaSharp는 메서드와 메서드를 RotateDegrees
RotateRadians
모두 지원합니다.
public void RotateDegrees (Single degrees)
public Void RotateRadians (Single radians)
360도의 원은 2π 라디안과 동일하므로 두 단위 간에 쉽게 변환할 수 있습니다. 어느 것이든 편리합니다. .NET Math
클래스의 모든 삼각 함수는 라디안 단위를 사용합니다.
회전은 각도를 높이기 위한 시계 방향입니다. (카티시안 좌표계의 회전은 규칙에 따라 시계 반대 방향으로 회전하지만, 시계 방향 회전은 SkiaSharp에서와 같이 Y 좌표가 감소하는 것과 일치합니다.) 음수 각도 및 360도보다 큰 각도가 허용됩니다.
회전에 대한 변환 수식은 변환 및 배율보다 더 복잡합니다. α 각도의 경우 변환 수식은 다음과 같습니다.
x' = x•cos(α) – y•sin(α)
y' = x•sin(α) + y•cos(α)
기본 회전 페이지에서 메서드를 RotateDegrees
보여 줍니다. BasicRotate.xaml.cs 파일은 기준선이 페이지 가운데에 있는 일부 텍스트를 표시하고 – 360에서 360까지의 범위를 기준으로 Slider
회전합니다. 처리기의 관련 부분은 PaintSurface
다음과 같습니다.
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextAlign = SKTextAlign.Center,
TextSize = 100
})
{
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
}
회전은 캔버스의 왼쪽 위 모서리를 중심으로 하므로 이 프로그램에서 설정된 대부분의 각도에서 텍스트가 화면에서 회전됩니다.
다음과 같은 버전의 RotateDegrees
RotateRadians
메서드를 사용하여 지정된 피벗 지점을 중심으로 회전하는 경우가 많습니다.
public void RotateDegrees (Single degrees, Single px, Single py)
public void RotateRadians (Single radians, Single px, Single py)
가운데 회전 페이지는 확장된 버전의 RotateDegrees
회전 중심을 텍스트 위치를 지정하는 데 사용되는 것과 동일한 지점으로 설정하는 데 사용된다는 점을 제외하고 기본 회전과 같습니다.
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextAlign = SKTextAlign.Center,
TextSize = 100
})
{
canvas.RotateDegrees((float)rotateSlider.Value, info.Width / 2, info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
}
이제 텍스트는 텍스트 기준선의 가로 가운데에 있는 텍스트를 배치하는 데 사용되는 지점을 중심으로 회전합니다.
메서드의 Scale
가운데 맞춤 버전과 마찬가지로, 호출의 RotateDegrees
중심 버전은 바로 가기입니다. 메서드는 다음과 같습니다.
RotateDegrees (degrees, px, py);
이 호출은 다음과 같습니다.
canvas.Translate(px, py);
canvas.RotateDegrees(degrees);
canvas.Translate(-px, -py);
경우에 따라 호출을 호출과 Rotate
결합 Translate
할 수 있습니다. 예를 들어 가운데 회전 페이지의 호출 및 DrawText
호출 은 RotateDegrees
다음과 같습니다.
canvas.RotateDegrees((float)rotateSlider.Value, info.Width / 2, info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
호출은 RotateDegrees
두 호출 Translate
및 비중심 RotateDegrees
호출과 동일합니다.
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.Translate(-info.Width / 2, -info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
특정 위치에 텍스트를 표시하는 호출은 DrawText
해당 위치에 대한 호출 다음에 지점(0, 0)을 호출하는 DrawText
것과 같습니다Translate
.
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.Translate(-info.Width / 2, -info.Height / 2);
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.DrawText(Title, 0, 0, textPaint);
두 번의 연속 Translate
호출은 서로를 취소합니다.
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.DrawText(Title, 0, 0, textPaint);
개념적으로 두 변환은 코드에 표시되는 방식과 반대되는 순서로 적용됩니다. 호출은 DrawText
캔버스의 왼쪽 위 모서리에 텍스트를 표시합니다. 호출은 RotateDegrees
왼쪽 위 모서리를 기준으로 해당 텍스트를 회전합니다. 그런 다음 호출은 Translate
텍스트를 캔버스의 가운데로 이동합니다.
일반적으로 회전과 변환을 결합하는 방법에는 여러 가지가 있습니다. 회전된 텍스트 페이지에서 다음 표시를 만듭니다.
클래스의 PaintSurface
처리기는 RotatedTextPage
다음과 같습니다.
static readonly string text = " ROTATE";
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint
{
Color = SKColors.Black,
TextSize = 72
})
{
float xCenter = info.Width / 2;
float yCenter = info.Height / 2;
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
float yText = yCenter - textBounds.Height / 2 - textBounds.Top;
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.Save();
canvas.RotateDegrees(degrees, xCenter, yCenter);
canvas.DrawText(text, xCenter, yText, textPaint);
canvas.Restore();
}
}
}
및 yCenter
값은 xCenter
캔버스의 중심을 나타냅니다. 값은 yText
해당 값의 약간 오프셋입니다. 이 값은 텍스트가 실제로 세로로 페이지 가운데에 배치되도록 텍스트를 배치하는 데 필요한 Y 좌표입니다. 그런 다음 루프는 for
캔버스의 중심에 따라 회전을 설정합니다. 회전은 30도씩 증가합니다. 값을 사용하여 텍스트를 그립니다 yText
. 이러한 12개 텍스트 문자열 간의 연결을 도데카곤으로 표시하기 위해 값의 "ROTATE" text
라는 단어가 경험적으로 결정되기 전의 공백 수입니다.
이 코드를 간소화하는 한 가지 방법은 호출 후 DrawText
루프를 통해 매번 회전 각도를 30도 증가시켜서입니다. 이렇게 하면 호출 및 Restore
.에 대한 호출 Save
이 필요하지 않습니다. 변수는 degrees
블록 본문 내에서 더 이상 사용되지 않습니다.for
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.DrawText(text, xCenter, yText, textPaint);
canvas.RotateDegrees(30, xCenter, yCenter);
}
루프 앞에 모든 항목을 캔버스 가운데 RotateDegrees
로 이동하라 Translate
는 호출과 함께 간단한 형식을 사용할 수도 있습니다.
float yText = -textBounds.Height / 2 - textBounds.Top;
canvas.Translate(xCenter, yCenter);
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.DrawText(text, 0, yText, textPaint);
canvas.RotateDegrees(30);
}
수정된 yText
계산은 더 이상 통합되지 않습니다 yCenter
. 이제 호출은 DrawText
텍스트를 캔버스 맨 위에 세로로 가운데에 배치합니다.
변환은 코드에 표시되는 방식과 반대로 개념적으로 적용되므로 더 많은 전역 변환과 더 많은 로컬 변환으로 시작할 수 있습니다. 이는 회전과 변환을 결합하는 가장 쉬운 방법입니다.
예를 들어 중심을 중심으로 회전하는 그래픽 개체를 축에서 회전하는 행성처럼 그리려는 경우를 가정해 보겠습니다. 그러나 이 물체가 태양을 중심으로 회전하는 행성처럼 화면의 중심을 중심으로 회전하기를 원합니다.
이렇게 하려면 캔버스의 왼쪽 위 모서리에 개체를 배치한 다음 애니메이션을 사용하여 해당 모퉁이를 돌면 됩니다. 다음으로, 개체를 궤도 반경처럼 가로로 변환합니다. 이제 원점 주위에도 두 번째 애니메이션 회전을 적용합니다. 이렇게 하면 개체가 모퉁이를 돌게 됩니다. 이제 캔버스의 가운데로 변환합니다.
다음은 이러한 변환 호출을 PaintSurface
역순으로 포함하는 처리기입니다.
float revolveDegrees, rotateDegrees;
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint fillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Red
})
{
// Translate to center of canvas
canvas.Translate(info.Width / 2, info.Height / 2);
// Rotate around center of canvas
canvas.RotateDegrees(revolveDegrees);
// Translate horizontally
float radius = Math.Min(info.Width, info.Height) / 3;
canvas.Translate(radius, 0);
// Rotate around center of object
canvas.RotateDegrees(rotateDegrees);
// Draw a square
canvas.DrawRect(new SKRect(-50, -50, 50, 50), fillPaint);
}
}
revolveDegrees
필드와 rotateDegrees
필드에 애니메이션이 적용됩니다. 이 프로그램은 클래스에 Xamarin.FormsAnimation
따라 다른 애니메이션 기술을 사용합니다. (이 클래스는 다음의 22장에서 설명합니다.Mobile AppsXamarin.Forms 만들기의 무료 PDF 다운로드 ) 재정의는 OnAppearing
콜백 메서드를 사용하여 두 개체 Animation
를 만든 다음 애니메이션 기간 동안 호출 Commit
합니다.
protected override void OnAppearing()
{
base.OnAppearing();
new Animation((value) => revolveDegrees = 360 * (float)value).
Commit(this, "revolveAnimation", length: 10000, repeat: () => true);
new Animation((value) =>
{
rotateDegrees = 360 * (float)value;
canvasView.InvalidateSurface();
}).Commit(this, "rotateAnimation", length: 1000, repeat: () => true);
}
첫 번째 Animation
개체는 10초 동안 0도에서 360도까지 애니메이션 revolveDegrees
효과를 줍니다. 두 번째 함수는 1초마다 0도에서 360도까지 애니메이션 rotateDegrees
효과를 주며 표면을 무효화하여 처리기에 대한 또 다른 호출을 PaintSurface
생성합니다. 재정의는 OnDisappearing
다음 두 애니메이션을 취소합니다.
protected override void OnDisappearing()
{
base.OnDisappearing();
this.AbortAnimation("revolveAnimation");
this.AbortAnimation("rotateAnimation");
}
추한 아날로그 시계 프로그램 (더 매력적인 아날로그 시계는 이후 문서에서 설명될 것이기 때문에 소위) 시계의 분 및 시간 표시를 그리고 손을 회전하는 회전을 사용합니다. 이 프로그램은 반경이 100인 점(0, 0)의 가운데에 있는 원을 기반으로 임의 좌표계를 사용하여 클록을 그립니다. 번역 및 크기 조정을 사용하여 페이지에서 해당 원을 확장하고 가운데에 배치합니다.
및 Scale
호출은 Translate
시계에 전역적으로 적용되므로 개체를 초기화 SKPaint
한 후 호출되는 첫 번째 호출입니다.
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint strokePaint = new SKPaint())
using (SKPaint fillPaint = new SKPaint())
{
strokePaint.Style = SKPaintStyle.Stroke;
strokePaint.Color = SKColors.Black;
strokePaint.StrokeCap = SKStrokeCap.Round;
fillPaint.Style = SKPaintStyle.Fill;
fillPaint.Color = SKColors.Gray;
// Transform for 100-radius circle centered at origin
canvas.Translate(info.Width / 2f, info.Height / 2f);
canvas.Scale(Math.Min(info.Width / 200f, info.Height / 200f));
...
}
}
시계 주위에 원에 그려야하는 두 가지 크기의 60 마크가 있습니다. 호출은 DrawCircle
시계의 중심을 기준으로 12:00에 해당하는 지점(0, –90)에서 해당 원을 그립니다. 이 호출은 RotateDegrees
각 눈금 표시 후 회전 각도를 6도 증가합니다. 변수는 angle
큰 원 또는 작은 원을 그릴지 여부를 결정하는 데만 사용됩니다.
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
...
// Hour and minute marks
for (int angle = 0; angle < 360; angle += 6)
{
canvas.DrawCircle(0, -90, angle % 30 == 0 ? 4 : 2, fillPaint);
canvas.RotateDegrees(6);
}
...
}
}
마지막으로 처리 PaintSurface
기는 현재 시간을 가져오고 시간, 분 및 초침의 회전 각도를 계산합니다. 각 손은 12:00 위치에 그려지므로 회전 각도는 다음과 같습니다.
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
...
DateTime dateTime = DateTime.Now;
// Hour hand
strokePaint.StrokeWidth = 20;
canvas.Save();
canvas.RotateDegrees(30 * dateTime.Hour + dateTime.Minute / 2f);
canvas.DrawLine(0, 0, 0, -50, strokePaint);
canvas.Restore();
// Minute hand
strokePaint.StrokeWidth = 10;
canvas.Save();
canvas.RotateDegrees(6 * dateTime.Minute + dateTime.Second / 10f);
canvas.DrawLine(0, 0, 0, -70, strokePaint);
canvas.Restore();
// Second hand
strokePaint.StrokeWidth = 2;
canvas.Save();
canvas.RotateDegrees(6 * dateTime.Second);
canvas.DrawLine(0, 10, 0, -80, strokePaint);
canvas.Restore();
}
}
손은 다소 조잡하지만 시계는 확실히 작동합니다.
더 매력적인 시계는 SkiaSharp의 SVG 경로 데이터 문서를 참조하세요.