在SwiftUI中使用相对坐标Shape绘制

在SwiftUI中使用相对坐标Shape绘制

Swift UI 提供了一些内置的基本形状,如矩形,圆角矩形,圆和椭圆等。 实际开发过程中,这些形状远远不够,需要增加新的定制形状,比如,三角形,或者复杂一点的水滴形状等。

要增加新的自定义形状,只要实现SwiftUI 提供Shape协议即可。下面是自定义一个三角形的例子:

swift 复制代码
struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let centerX = rect.width / 2
        let centerY = rect.height / 2
        path.move(to: CGPoint(x: centerX, y: 0))
        path.addLine(to: CGPoint(x: 0, y: centerY))
        path.addLine(to: CGPoint(x: centerX, y: rect.height))
        path.closeSubpath()
        return path
    }
}

在View中可以直接使用上述三角形:

swift 复制代码
#Preview
{
    HStack
    {
        Triangle()
            .stroke(Color.red)
            .frame(width: 100, height: 100)
            .background(Color.gray.opacity(0.  3))
  }   
}

显示效果如下:

在Shape协议中,所有的形状都在一个容器矩形(CGRect)中绘制。 绘制的关键是在如何确定形状中的一些关键点,并最终用容器矩形的四个参数minX,minY,maxX,maxY表达出这些关键点。 (矩形本身的长和宽也可以用上面四个参数表达)

上面绘制三角形的代码其实很简单,这主要是因为三角形很简单。但仔细观察 ,可以发现绘制代码并不好理解, 代码中充斥着各种CGPoint,这些CGPoint有些是关键点,有些是辅助点,它们的坐标需要根据 rect计算得出,都需要我们转几个弯才能理解。

想象一下,如果定制形状比较复杂,特别是一些有控制点的曲线形状,上述计算CGPoint的过程就非常容易出错,而且由于都是变量,对这些CGPoint在rect中的位置也没有直观认识。如果这些形状还需要一些外部参数控制的话,情况可能会变得更糟糕。

使用相对坐标简化绘制

使用相对坐标可以解决上述问题。我们首先引进相对坐标的概念,定义屏幕上任意点(X,Y)相对容器矩形rect的相对坐标为(ratioX,ratioY),其中,ratiXratioY 定义如下:

swift 复制代码
ratioX  = ( X - minX )/(maxX-minX)
ratioY  = ( Y - minY )/(maxY-minY)

相对坐标这个名称其实不尽合理,从数学上看,归一坐标或者标准坐标或正则坐标也许更合理些,不过这些名词不那么接地气。

相对坐标的主要思路是,将容器矩形rect,无论大小,都缩放到一个单位矩形,这在数学上可以对rect的坐标系进行了两次变换,一次平移变换,一次伸缩变换来实现。

容易看出,如果点(X,Y)位于矩形rect内,则其相对坐标分量均大于等于0且小于等于1 。实际上,点(X,Y)可以是屏幕上任何位置,可以在矩形内也可以在矩形外,因此其相对坐标不必都在0和1之间,大于1,小于0均是有可能的。实际上,由于形状的绘制均在容器矩形内,所以,其相对坐标的范围也就都在0和1之间了。

比较一下分别用相对坐标和绝对坐标表达的容器矩形中的几个关键点 :

位置 绝对坐标 相对坐标
矩形左下角 (minX,minY) (0,0)
矩形右上角 (maxX,maxY) (1,1)
矩形中心 (midX,midY) (0.5,0.5)

可以看到,相对于绝对坐标,相对坐标的表达更加简单,清晰直观。这正是我们简化Shape形状绘制的基础。

使用相对坐标进行绘制

如果已经知道相对坐标(ratioX,ratioY),则我们很容易用下面公式计算其绝对坐标:

swift 复制代码
X   =   minX + ratioX*(maxX-minX)
Y   =   minY + ratioY*(maxY-minY)

为了使用相对坐标进行绘制,我们必须按上述公式将相对坐标转换成绝对坐标,为此,首先对rect进行扩展,使之具有处理相对坐标的能力:

swift 复制代码
extension CGRect
{
    func xy(_ ratioX:CGFloat,_ ratioY:CGFloat )->CGPoint
    {
        return CGPoint (x: self.minX + ratioX * self.width ,y:self.minY + ratioY  * self.height )
    }
     
}     
  //备注容器矩形的长和宽
  //width   =  maxX - minX  
  //height  =  maxY - minY  

上述扩展为CGRect类增加一个xy()的方法,该函数接受相对坐标为参数,用我们的上述公式将该相对坐标转换为绝对坐标,用CGPoint对象表示,各种具体的绘制方法均以CGPoint 对象为基础进行。有了上述扩展后,我们就可以用相对坐标绘制了。

下面是使用相对坐标后的三角形绘制代码,

swift 复制代码
struct TriangleA: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: rect.xy(0.5, 0))
        path.addLine(to: rect.xy(0, 0.5))
        path.addLine(to: rect.xy(0.5, 1)) 
        path.closeSubpath()
        return path
    }
}

可以看到,新代码不仅更简洁,更清晰,也更容易理解了。

同时使用两种方式绘制三角形:

swift 复制代码
#Preview
{
    HStack
    {
        //按绝对坐标绘制的三角形
        Triangle()
            .stroke(Color.red)
            .frame(width: 100, height: 100)
            .background(Color.gray.opacity(0.3))
        //按相对坐标绘制的三角形
         TriangleA()
            .stroke(Color.blue)
            .frame(width: 100, height: 100)
            .background(Color.gray.opacity(0.3))
   }   
    
}

下面是显示效果,红色色使用绝对坐标绘制的三角形,蓝色是使用相对坐标绘制的三角形。可以看到,二者绘制效果完全一致:

相关推荐
天天打码几秒前
Bootstrap Table开源的企业级数据表格集成
前端·开源·bootstrap
Allen Bright3 分钟前
【CSS-8】深入理解CSS选择器权重:掌握样式优先级的关键
前端·css
hnlucky5 分钟前
安装vue的教程——Windows Node.js Vue项目搭建
前端·javascript·vue.js·windows·node.js
余道各努力,千里自同风18 分钟前
CSS“多列布局”
前端·css·html
Keya24 分钟前
使用 tinypng 脚本打包为exe 进行压缩图片
前端·python·程序员
wordbaby30 分钟前
React Router 的 handle 和 useMatches 的作用、场景和联系
前端·react.js
我的div丢了肿么办30 分钟前
ResizeObserver和IntersectionObserver的详细讲解
前端·javascript·vue.js
凌览31 分钟前
斩获 7k Star,这个桌宠项目火了🔥
前端·javascript·后端
opbr32 分钟前
🔥 扔掉虚拟DOM!百行代码实现高性能Vue内核,直接操作DOM竟这么香?
前端
Struggler28137 分钟前
cloudflare r2进行cdn加速
前端