上期我们利用FFImageLoad实现了图片流的显示,之前也有一期简单介绍了一下利用SkiaSharp实现绘图。
本期我们来实现一个摇杆的实现。 **如有问题或者需要源码可以到交流群 : ** ** ** 656210280 探讨 ** ** ** **
public class DrawAble{ private readonly Rocker view; public DrawAble(Rocker view) { this.view = view; }
private void Draw_Circle(SKSurface surface, SKRect bounds) { float centerX = bounds.MidX; float centerY = bounds.MidY; Midx = centerX; Midy = centerY; // 计算圆的半径(使用ChargingRingDrawable类中的rad属性) float radius = CirCleRad;
// 使用SKPaint对象定义圆的样式(颜色、线条宽度等) using (SKPaint paint = new SKPaint()) { paint.Color = SKColors.Blue; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 20; // 画圆 surface.Canvas.DrawCircle(centerX, centerY, radius, paint); } }
public void Draw(SKSurface surface, SKRect bounds) { surface.Canvas.Clear(); Draw_Circle(surface, bounds);//画圆轮廓 }}
首先定义两个类,一个是画板类,他必须有最基本的Draw函数用来给Sharp实现接口。
其构造函数传入我们的Rocker类,这个类我们在下面定义。
public partial class Rocker : SKCanvasView { private readonly DrawAble drawable; public Rocker() { this.EnableTouchEvents = true;// this.drawable = new ChargingRingDrawable(this); } protected override void OnPaintSurface(SKPaintSurfaceEventArgs e) { this.drawable.Draw(e.Surface, e.Info.Rect); this.InvalidateSurface(); }
protected override void OnTouch(SKTouchEventArgs e) { base.OnTouch(e); }
}}
接着定义一个类,命名为Rocker,这个类抽象自SKCanvasView(SkiaSharp的控件) 定义一个画板,构造的时候传入自身。
我们要保留两个处理函数,一个是OnPaintSurface,我们的控件刷新就会调用这个函数,还有一个是OnTouch函数,这个函数用来处理我们的触摸事件。
private void Draw_Circle(SKSurface surface, SKRect bounds){ float centerX = bounds.MidX;//获得画板中心 float centerY = bounds.MidY; Midx = centerX;//用一个参数保存画板中心 Midy = centerY; // 计算圆的半径(使用ChargingRingDrawable类中的rad属性) float radius = CirCleRad;
// 使用SKPaint对象定义圆的样式(颜色、线条宽度等) using (SKPaint paint = new SKPaint()) { paint.Color = SKColors.Blue; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 20; // 画圆 surface.Canvas.DrawCircle(centerX, centerY, radius, paint); }}
在DrawAble类中有这样子一个函数,其作用是画一个基本的圆,我设置的大小是400像素。
private void Draw_InsertCircle(SKCanvas canvas, SKPoint touchLocation, float radius){ // 控件的中心点 float centerX = Midx; float centerY = Midy;
// 计算手的位置与圆的交点 SKPoint intersectionPoint = CalculateIntersectionPoint(centerX, centerY, 300, touchLocation);
// 使用SKPaint对象定义圆的样式(颜色、线条宽度等) using (SKPaint paint = new SKPaint()) { paint.Color = SKColors.Red; paint.Style = SKPaintStyle.Fill;
// 在交点位置绘制圆 canvas.DrawCircle(intersectionPoint.X, intersectionPoint.Y, radius, paint); LocationPoint = intersectionPoint; }}
private SKPoint CalculateIntersectionPoint(float centerX, float centerY, float radius, SKPoint touchLocation){ // 计算手的位置与圆的交点 float dx = touchLocation.X - centerX; float dy = touchLocation.Y - centerY; float distance = (float)Math.Sqrt(dx * dx + dy * dy);
// 如果距离超出阈值,将交点移动到圆上最近的点 if (distance > radius) { float scale = radius / distance; float intersectionX = centerX + dx * scale; float intersectionY = centerY + dy * scale; return new SKPoint(intersectionX, intersectionY); }
// 如果距离未超出阈值,则返回手的位置作为交点 return touchLocation;}
接着画内部的摇杆内容,我们计算这个圆和控件中心的位置,设置一个阈值,保证我们画的圆在这个阈值之内,如果超过了阈值,就计算手的位置和圆的交点,再进行画圆。
实现这样子的效果。
接着,我们补全Rocker中触摸事件
protected override void OnTouch(SKTouchEventArgs e){ base.OnTouch(e);
switch (e.ActionType) { case SKTouchAction.Pressed: // 处理按下事件 break;
case SKTouchAction.Moved: // 处理移动事件 SKPoint touchLocation = e.Location; drawable.InsertCirclePoint = touchLocation; OnPositionChanged(new SKPoint(drawable.LocationPoint.X-drawable.Midx,drawable.LocationPoint.Y-drawable.Midy)); break;
case SKTouchAction.Released: SKPoint InsertCirclePoint = new SKPoint(drawable.Midx,drawable.Midy); drawable.InsertCirclePoint = InsertCirclePoint; // 使得画布无效,触发重绘 InvalidateSurface(); OnPositionChanged(new SKPoint(0,0)); break;
case SKTouchAction.Cancelled: // 处理取消事件 break; }
// 标记事件已处理 e.Handled = true;}
当移动时,将位置传给DrawAble中,DrawAble会根据手的位置绘制摇杆位置。
并且在放下的时候重新归位。 同时我们定义一个事件,向MainPage中传入位置信息。
private void Rocker_PositionChanged(object sender, SKPoint newPosition){ Label.Text = $"Position: ({newPosition.X}, {newPosition.Y})";}
MainPage中打印我们的位置信息。
效果展示 ** 源码 **
using Microsoft.Maui.Animations;using SkiaSharp.Views.Maui;using SkiaSharp;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SkiaSharp.Views.Maui.Controls;using System.Runtime.InteropServices;
namespace MAUIapp.View{
public class DrawAble { private readonly Rocker view; private SKImage tmp; public int rad = 400; private int CirCleRad = 400; public SKPoint InsertCirclePoint; public SKPoint LocationPoint; public float Midx; public float Midy;
public DrawAble(Rocker view) { this.view = view; InsertCirclePoint = new SKPoint(0 / 2f, 0 / 2f); }
private void Draw_Circle(SKSurface surface, SKRect bounds) { float centerX = bounds.MidX; float centerY = bounds.MidY; Midx = centerX; Midy = centerY; // 计算圆的半径(使用ChargingRingDrawable类中的rad属性) float radius = CirCleRad;
// 使用SKPaint对象定义圆的样式(颜色、线条宽度等) using (SKPaint paint = new SKPaint()) { paint.Color = SKColors.Blue; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 20; // 画圆 surface.Canvas.DrawCircle(centerX, centerY, radius, paint); } }
private void Draw_InsertCircle(SKCanvas canvas, SKPoint touchLocation, float radius) { // 控件的中心点 float centerX = Midx; float centerY = Midy;
// 计算手的位置与圆的交点 SKPoint intersectionPoint = CalculateIntersectionPoint(centerX, centerY, 300, touchLocation);
// 使用SKPaint对象定义圆的样式(颜色、线条宽度等) using (SKPaint paint = new SKPaint()) { paint.Color = SKColors.Red; paint.Style = SKPaintStyle.Fill;
// 在交点位置绘制圆 canvas.DrawCircle(intersectionPoint.X, intersectionPoint.Y, radius, paint); LocationPoint = intersectionPoint; } }
private SKPoint CalculateIntersectionPoint(float centerX, float centerY, float radius, SKPoint touchLocation) { // 计算手的位置与圆的交点 float dx = touchLocation.X - centerX; float dy = touchLocation.Y - centerY; float distance = (float)Math.Sqrt(dx * dx + dy * dy);
// 如果距离超出阈值,将交点移动到圆上最近的点 if (distance > radius) { float scale = radius / distance; float intersectionX = centerX + dx * scale; float intersectionY = centerY + dy * scale; return new SKPoint(intersectionX, intersectionY); }
// 如果距离未超出阈值,则返回手的位置作为交点 return touchLocation; }
public void Draw(SKSurface surface, SKRect bounds) { surface.Canvas.Clear(); Draw_Circle(surface, bounds);//画圆轮廓 Draw_InsertCircle(surface.Canvas, InsertCirclePoint, 100); //surface.Snapshot(); }
}
public delegate void PositionChangedEventHandler(object sender, SKPoint newPosition);
public partial class Rocker : SKCanvasView { private readonly DrawAble drawable;
public event PositionChangedEventHandler PositionChanged;
public Rocker() { this.EnableTouchEvents = true; this.drawable = new DrawAble(this); }
protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height);
// 在这里获取控件的宽度和高度 float controlWidth = (float)width; float controlHeight = (float)height; // 使用宽度和高度初始化 InsertCirclePoint SKPoint InsertCirclePoint = new SKPoint(controlWidth / 2f, controlHeight / 2f); drawable.InsertCirclePoint = InsertCirclePoint; }
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e) { this.drawable.Draw(e.Surface, e.Info.Rect); this.InvalidateSurface(); }
protected override void OnHandlerChanged() { }
protected override void OnTouch(SKTouchEventArgs e) { base.OnTouch(e);
switch (e.ActionType) { case SKTouchAction.Pressed: // 处理按下事件 break;
case SKTouchAction.Moved: // 处理移动事件 SKPoint touchLocation = e.Location; drawable.InsertCirclePoint = touchLocation; OnPositionChanged(new SKPoint(drawable.LocationPoint.X-drawable.Midx,drawable.LocationPoint.Y-drawable.Midy)); break;
case SKTouchAction.Released: SKPoint InsertCirclePoint = new SKPoint(drawable.Midx,drawable.Midy); drawable.InsertCirclePoint = InsertCirclePoint; // 使得画布无效,触发重绘 InvalidateSurface(); OnPositionChanged(new SKPoint(0,0)); break;
case SKTouchAction.Cancelled: // 处理取消事件 break; }
// 标记事件已处理 e.Handled = true; }
protected virtual void OnPositionChanged(SKPoint newPosition) { PositionChanged?.Invoke(this, newPosition); }
}}