You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SkiaSharp DrawPath点击识别:如何判断用户点击的具体路径?

实现SkiaSharp路径点击识别的解决方案

要解决SkiaSharp中无法区分点击路径的问题,核心思路是保存所有可点击路径的实例,在点击时通过坐标转换和路径包含测试来判断点击的区域,具体步骤如下:

1. 调整XAML绑定点击事件

给TapGestureRecognizer添加Tapped事件处理方法,用于响应点击操作:

<skia:SKCanvasView PaintSurface="OnCanvasViewPaintSurface" VerticalOptions="FillAndExpand" >
    <skia:SKCanvasView.GestureRecognizers>
        <TapGestureRecognizer x:Name="tapGestureRecognizer" Tapped="OnTapGestureRecognizerTapped" />
    </skia:SKCanvasView.GestureRecognizers>
</skia:SKCanvasView>

2. 持久化保存可点击路径

在页面类中添加字段,保存所有路径的实例、区域标识和当前颜色,避免绘制后丢弃路径:

// 存储路径、区域ID、当前显示颜色
private List<(SKPath Path, string RegionId, SKColor CurrentColor)> _clickablePaths = new();

3. 修改绘制逻辑,复用保存的路径

调整OnCanvasViewPaintSurface方法,首次绘制时初始化路径列表,后续重绘使用保存的颜色状态:

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

    canvas.Clear();
    
    // 仅首次绘制时初始化路径
    if (_clickablePaths.Count == 0)
    {
        // 轮胎区域(region 72)
        var tirePath = SKPath.ParseSvgPathData("M52 152.1c-8.8 1.5-19.4 7.1-25.9 13.7-24.4 25.1-14.7 66.8 18.3 78.2 28.1 9.6 58-8.3 62.6-37.4 4-25.5-12.2-48.7-37.8-54.2-7.5-1.5-9.3-1.6-17.2-.3zm19.7 19.8c19.8 9 23.8 34.3 7.7 49-12 11-29.9 10.2-40.8-1.9-11.1-12.3-10.7-30.2 1.1-41.1 6.5-6 11.4-7.9 20.3-7.9 5.2 0 8.8.6 11.7 1.9z");
        _clickablePaths.Add((tirePath, "72", SKColors.Black));
        
        // 轮圈区域(region 73)
        var wheelPath = SKPath.ParseSvgPathData("M55.2 172c-1.3.6-2.8.8-3.3.4-.5-.3-.9 0-.9.6s-.4.8-1 .5c-.5-.3-1-.1-1 .4 0 .6-.9 1.1-1.9 1.1-1 0-2.1.7-2.5 1.5-.3.8-1.2 1.5-1.9 1.5-.8 0-1.9 1.1-2.5 2.5-.6 1.4-1.6 2.5-2.1 2.5-1.3 0-4.3 6.8-5.3 11.8-1.1 5.9 2 19.2 4.5 19.2.5 0 .6.4.3.9-.7 1.1 7.2 8.1 9.1 8.1.7 0 1.3.4 1.3.8 0 .5.6.9 1.3 1 .6.1 3.5.6 6.2 1.2 5.5 1.2 13.5.5 13.5-1.2 0-.6.3-.9.6-.5.4.3 1.3-.1 2.1-.9s1.9-1.2 2.4-.9c.5.4.9.1.9-.6s.3-1 .6-.6c.9.8 8.5-7.4 7.8-8.5-.3-.4 0-.8.7-.8s1-.3.6-.6c-.3-.4 0-1.2.7-2 3.5-3.5 2.5-19.3-1.6-23.8-1-1-1.5-2.4-1.1-2.9.3-.6.1-.7-.5-.3-.7.4-1.2.1-1.2-.8s-.5-1.5-1.2-1.4c-.7.2-1.2-.3-1.3-1 0-.6-.4-1.3-1-1.4-.5-.1-1.8-.9-2.8-1.8-3.3-3.1-15.6-5.6-19.5-4z");
        _clickablePaths.Add((wheelPath, "73", StringToColor("#E7FFFF")));
        
        // 外围黑线区域(region 14)
        var linePath = SKPath.ParseSvgPathData("M111 112.6c-20.7 2.6-23.5 3.6-32.6 11.5l-5.5 4.9h-7.3c-9.1 0-9.8.7-9.4 9.2l.3 6.3 7.5.8c29.3 3.2 50.1 25.6 50 54-.1 26-19.5 48.8-45 52.8-4.1.6-8.1 1.3-8.8 1.5-1.5.4-1.8 30.4-.2 30.4.6 0 1-5.5 1-14.4v-14.3l6.1-.7c22.7-2.6 41.7-19.7 47.5-42.6 2.2-8.7 1.5-23.2-1.6-31.5-3-8.1-10.1-18.6-15.8-23.5-9.4-8.1-24.3-13.9-35.5-14h-3.8l.3-5.8.3-5.7 6-.3c8.7-.3 9.8-.7 15.7-5.8 8.6-7.4 10.6-8.1 32.4-10.9 28.8-3.6 32.2-3.6 36.6-.3 2 1.5 4.5 4.7 5.7 7 3.6 7.5 17.2 75.7 26.7 133.5 2.6 16.1 5.2 29.3 5.6 29.3 1.3 0 1-2.6-4.6-36.2-6.4-37.6-12.7-71.1-19.5-103-4.2-19.4-5.4-23.3-8-27.2-1.8-2.5-4.5-5.3-6.1-6.1-4.1-2.1-15.8-1.8-38 1.1z");
        _clickablePaths.Add((linePath, "14_line", SKColors.Black));
        
        // 蓝色区域(region 14)
        var restPath = SKPath.ParseSvgPathData("M133.5 112.6c-1.1.2-10.5 1.3-21 2.5-21.1 2.5-22.9 3.1-32.2 10.8-6 5-6.2 5.1-12.6 5.1-9.3 0-9.7.3-9.7 6.6v5.4h5.3c20.2 0 41.1 14.8 49.3 35 3.6 8.8 4.6 27.4 1.9 36.6-5.5 19.3-20.6 33.9-40 38.9-3.2.8-7.6 1.5-9.7 1.5H61v29h32v-20.1l4.3.3 4.2.3.3 9.7.3 9.8H186l-.5-2.8c-.3-1.5-3.2-18.7-6.5-38.2-5.9-35.7-13.2-73.8-19.6-103.5-5.3-24.6-10-29.4-25.9-26.9zM144 255v11h-8v-22h8v11z");
        _clickablePaths.Add((restPath, "14_rest", StringToColor("#6699FF")));
    }
            
    using (SKPaint paint = new SKPaint())
    {
        foreach (var pathItem in _clickablePaths)
        {
            // 根据区域设置绘制样式
            paint.Style = pathItem.RegionId is "73" or "14_line" 
                ? SKPaintStyle.Stroke 
                : SKPaintStyle.StrokeAndFill;
            
            paint.Color = pathItem.CurrentColor;
            paint.StrokeWidth = 1;
            
            canvas.DrawPath(pathItem.Path, paint);
        }
    }
}

4. 实现点击识别与颜色切换

添加点击事件处理方法,转换触摸坐标并判断点击的路径,修改颜色后触发重绘:

private void OnTapGestureRecognizerTapped(object sender, EventArgs e)
{
    if (sender is not SKCanvasView canvasView || e is not TappedEventArgs tappedArgs) return;
    
    // 将Xamarin视图坐标转换为Skia画布坐标
    var xamarinPoint = tappedArgs.GetPosition(canvasView);
    var scale = canvasView.CanvasSize.Width / canvasView.Width;
    var skPoint = new SKPoint((float)(xamarinPoint.X * scale), (float)(xamarinPoint.Y * scale));
    
    // 遍历路径判断点击区域
    foreach (var pathItem in _clickablePaths)
    {
        bool isHit;
        if (pathItem.RegionId is "73" or "14_line")
        {
            // 描边路径需要传入笔触宽度进行精确判断
            using var paint = new SKPaint { StrokeWidth = 1 };
            isHit = pathItem.Path.Contains(skPoint, paint);
        }
        else
        {
            // 填充路径直接判断点是否在内部
            isHit = pathItem.Path.Contains(skPoint);
        }
        
        if (isHit)
        {
            // 切换颜色(示例:点击变红,再点击恢复原色)
            pathItem.CurrentColor = pathItem.CurrentColor == SKColors.Red 
                ? GetOriginalColor(pathItem.RegionId) 
                : SKColors.Red;
            
            // 触发画布重绘
            canvasView.InvalidateSurface();
            break;
        }
    }
}

// 获取区域原始颜色的辅助方法
private SKColor GetOriginalColor(string regionId)
{
    return regionId switch
    {
        "72" => SKColors.Black,
        "73" => StringToColor("#E7FFFF"),
        "14_line" => SKColors.Black,
        "14_rest" => StringToColor("#6699FF"),
        _ => SKColors.Transparent
    };
}

关键注意事项

  • 路径持久化:必须保存每个SKPath实例,不能在绘制后丢弃,否则无法进行点击包含测试。
  • 坐标转换:Xamarin的触摸坐标是视图相对坐标,需要按画布缩放比例转换为Skia绝对坐标,避免点击判断偏移。
  • 描边路径判断:对于描边样式的路径,需使用Contains(SKPoint, SKPaint)重载,传入与绘制时一致的笔触宽度,确保点击范围准确。

内容的提问来源于stack exchange,提问作者GuidoG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 15:20:28