應用 SwiftUI Path API 繪製撲克牌的四種花色!


本篇原文(標題:SwiftUI: How to draw playing Cards shades using Path API?)刊登於作者 Medium,由 Prafulla Singh 所著,並授權翻譯及轉載。

在 SwiftUI 中,我們可以利用 Path 和 Shape 來客製化渲染 (rendering),而 Path 又可以用來製作 Shape,也就是說,Path 就是基本的繪圖元素。

在這篇教學文章中,我們將會使用 SwiftUI Path 來建立撲克牌的四種花色。首先,讓我們來看看可以繪製甚麼類型的 Path。

  • Line 會添加一條直線。
func addLine(to point: CGPoint)
  • Arc 會從中心添加一條圓弧,需要角度、半徑、中心點、和方向的資料。
func addArc(withCenter center: CGPoint, 
     radius: CGFloat, 
 startAngle: CGFloat, 
   endAngle: CGFloat, 
  clockwise: Bool)
  • Quad Curve 會添加一元二次方程式的曲線。一元二次方程式需要三個點,來繪製一條 Path。
quad-curve
圖片來源:Apple Documentation
func addQuadCurve(to endPoint: CGPoint, 
     controlPoint: CGPoint)
  • Cubic Curve 會添加三次方程式的曲線。一個三次方程式需要四個點,來繪製一條 Path。
swiftui-path-cubic-curve
func addCurve(to endPoint: CGPoint, 
controlPoint1: CGPoint, 
controlPoint2: CGPoint)

現在,讓我們嘗試使用上面的方法,來創建撲克牌花色的圖案。 但是在開始繪製之前,先來介紹一些我們將會在練習中多次使用的函式。

import SwiftUI

extension CGRect {
    /// center of rect
    var center: CGPoint {
        return CGPoint(x: self.midX, y: self.midY)
    }
    /// if rect is not square take centered square to draw
    var centeredSquare: CGRect {
        let width = ceil(min(size.width, size.height))
        let height = width

        let newOrigin = CGPoint(x: origin.x + (size.width - width) / 2, y: origin.y + (size.height - height) / 2)
        let newSize = CGSize(width: width, height: height)
        return CGRect(origin: newOrigin, size: newSize)
    }
    
    func flatten() -> (CGFloat, CGFloat, CGFloat, CGFloat) { 
        return (origin.x, origin.y, size.width, size.height)
    }
}

extension Angle {
    
    static let A180 = Angle(radians: .pi) //180
    
    static let A90 = Angle(radians: .pi/2) //90
    
    static let A270 = Angle(radians: (.pi/2) * 3) //270
    
    static let A360 = Angle(radians: .pi * 2) //360
}

方塊花色 (diamond shade) 是最容易繪製的花色。我們可以使用簡單的 Line、Curve、或 Arc 來繪製。在這個範例中,我們會使用 Arc。

  1. 移動到底部的中間,或任何一條邊的中間(左側、右側、或頂部)。
  2. 以角落為中心,向下一條邊的中心繪製圓弧。
import SwiftUI

struct Diamond: Shape {
    
    func path(in rect: CGRect) -> Path {
        let (x, y, width, height) = rect.centeredSquare.flatten()
        let lowerPoint = CGPoint(x: x + width / 2, y: (y + height ))
        
        let path = Path { p in
            p.move(to: lowerPoint)
            p.addArc(center: CGPoint(x: x, y: (y + height)),
                radius: (width / 2),
                startAngle: .A360,
                endAngle: .A270,
                clockwise: true)
            p.addArc(center: CGPoint(x: x, y: y),
                radius: (width / 2),
                startAngle: .A90,
                endAngle: .zero,
                clockwise: true)
            p.addArc(center: CGPoint(x: x + width, y: y),
                radius: (width / 2),
                startAngle: .A180,
                endAngle: .A90,
                clockwise: true)
            p.addArc(center: CGPoint(x: x + width, y: y + height),
                radius: (width / 2),
                startAngle: .A270 ,
                endAngle: .A180,
                clockwise: true)
        }
        return path
    }
    
}

紅心花色 (heart shade) 是兩條 Cubic Curve 和兩條 Arc 的組合。

  1. 移動到底部的中間。
  2. 向左邊或右邊中間繪製 Cubic Curve,保持角度在45度。
  3. 從上一點繪製一條 180° 的 Arc,半徑為寬度的 25%,中心則為長度和寬度的 25%。
  4. 反向重複,也就是重複第三步、第二步。
import SwiftUI

struct Heart: Shape {
    func path(in rect: CGRect) -> Path {
        
        let (x, y, width, height) = rect.centeredSquare.flatten()
        let lowerPoint = CGPoint(x: x + width / 2, y: (y + height ))
        
        let path = Path { p in
            p.move(to: lowerPoint)
            p.addCurve(to: CGPoint(x: x, y: (y + (height / 4))),
                  control1: CGPoint(x: (x + (width / 2)), y: (y + (height * 3 / 4))),
                  control2: CGPoint(x: x, y: (y + (height / 2))))
            p.addArc(center: CGPoint(x: (x + (width / 4)), y: (y + (height / 4))),
                radius: (width / 4),
                startAngle: .A180,
                endAngle: .zero,
                clockwise: false)
            p.addArc(center: CGPoint(x: (x + (width * 3 / 4)), y: (y + (height / 4))),
                radius: (width / 4),
                startAngle: .A180,
                endAngle: .zero,
                clockwise: false)
            p.addCurve(to: lowerPoint,
                  control1: CGPoint(x: (x + width), y: (y + (height / 2))),
                  control2: CGPoint(x: (x + (width / 2)), y: (y + (height * 3 / 4))))
        }
        return path
    }
    
}

我們可以組合紅心花色和長方型,來完成黑桃花色 (Spade Shade)

  1. 利用 90% 高度建立一個心形。
  2. 把心形旋轉 180°,並獲取 Path。
  3. 從中心到底部的中心建立一個長方形,並設定適當的寬度。
  4. 合併兩條 Path。
import SwiftUI

struct Spade: Shape {
    func path(in rect: CGRect) -> Path {
        
        let (x, y, width, height) = rect.centeredSquare.flatten()
        
        var heartPath = Heart().rotation(.A180).path(in: CGRect(x: x, y: y, width: width, height: height * 0.9)) //take 10% for bottom rect
        let rectPath = Rectangle().path(in: CGRect(x: x + width * 0.4, y: y + height * 0.5, width: width * 0.2, height: height * 0.5))
        
        heartPath.addPath(rectPath)
        return heartPath
    }
}

梅花花色 (Club Shade) 就是由三個圓形和一個三角形組成的。

  1. 將 rect 分成 2×2 網格
  2. 以 0-1 網格的交叉點為直徑,繪製一個圓形
  3. 以 0-2 網格的交叉點為直徑,繪製一個圓形
  4. 以 1-3 網格的交叉點為直徑,繪製一個圓形
  5. 從中心到底部繪製一個 60° 的等邊三角形
import SwiftUI

struct Club: Shape {
    func path(in rect: CGRect) -> Path {
        
        let (x, y, width, height) = rect.centeredSquare.flatten()
        let center = rect.centeredSquare.center
        
        let center1 = CGPoint(x: x + width / 2, y: (y + height/4 ))
        let center2 = CGPoint(x: x + width / 4, y: (y + height/2 ))
        let center3 = CGPoint(x: x + width * 3 / 4, y: (y + height/2 ) )
        let radius =  (width / 4)
        let path = Path { p in
            p.move(to: center)
            p.addArc(center: center1,
                     radius: radius,
                startAngle: .A360,
                endAngle: .zero,
                clockwise: true)
            p.addArc(center: center2,
                    radius: radius,
                    startAngle: .A360,
                    endAngle: .zero,
                    clockwise: true)
            p.addArc(center: center3,
                     radius: radius,
                 startAngle: .A360,
                    endAngle: .zero,
                    clockwise: true)
            p.move(to: center)
            p.addLine(to: CGPoint(x: x + width / 4, y: y + height))
            p.addLine(to: CGPoint(x: x + width * 3 / 4, y: y + height))
            p.addLine(to: center)
        }
        
        return path
    }
}

你可以在 GitHub 上參考完整範例。

本篇原文(標題:SwiftUI: How to draw playing Cards shades using Path API?)刊登於作者 Medium,由 Prafulla Singh 所著,並授權翻譯及轉載。

作者簡介:Prafulla Singh,Block.one 的 iOS 開發者

譯者簡介:Kelly Chan-AppCoda 編輯小姐。


此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。

blog comments powered by Disqus
Shares
Share This