# Drawing and Animation
建議搭配 Apple Tutorial 服用,下載官方範例程式,從 StartingPoint 開始做起,前置作業的檔案都可以在 Resources 資料夾下找到。
## Drawing Paths and Shapes
[🔗 Apple Tutorial](https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes)
### 畫直線
教學中的六角形有點嚇人,我們可以先從畫四角形開始。`move` 可以想成移動你的畫筆到定點位置,`addLine` 是從目前位置畫一條直線到目標位置。
```swift=
struct Canvas: View {
let gradientEnd = Color(hex: 0x0093E9)
let gradientStart = Color(hex: 0x80D0C7)
var body: some View {
Path { path in
path.move(to: CGPoint(x: 100, y: 300))
path.addLine(to: CGPoint(x: 300, y: 300))
path.addLine(to: CGPoint(x: 300, y: 100))
path.addLine(to: CGPoint(x: 100, y: 100))
}
}
}
```
1. [Path](https://developer.apple.com/documentation/swiftui/path) The outline of a 2D shape.
2. [move(to:)](https://developer.apple.com/documentation/swiftui/path/move(to:)) Begins a new subpath at the specified point.
3. [addLine(to:)](https://developer.apple.com/documentation/swiftui/path/addline(to:)) Appends a straight line segment from the current point to the specified point.
填滿顏色,會將終點連接到起始點,所組成的區域填滿顏色
```swift=
.fill(.blue)
```
也可以上漸層色,漸層方式有很多種,可以通過下方連結了解更多
```swift=
// 線性漸層
.fill(.linearGradient(colors: [gradientStart, gradientEnd],
startPoint: .topLeading,
endPoint: .bottomTrailing))
// 中心向外漸層
.fill(.radialGradient(colors: [gradientStart, gradientEnd],
center: .center,
startRadius: 15,
endRadius: 80))
```
1. [ShapeStyle](https://developer.apple.com/documentation/swiftui/shapestyle) A color or pattern to use when rendering a shape.

填滿外框
```swift=
.stroke(Color(hex: "0093E9"), lineWidth: 10)
```

### 畫曲線
一元二次方程式的曲線
```swift=
Path { path in
path.move(to: CGPoint(x: 100, y: 100))
path.addQuadCurve(to: CGPoint(x: 300, y: 300), control: CGPoint(x: 100, y: 300))
}
.stroke(Color(hex:"0093E9"), lineWidth: 10)
```
1. [addQuadCurve(to:control:)](https://developer.apple.com/documentation/swiftui/path/addquadcurve(to:control:)) Adds a quadratic Bézier curve to the path, with the specified end point and control point.

一元三次方程式的曲線
```swift=
path in
path.move(to: CGPoint(x: 0, y: 0))
path.addCurve(to: CGPoint(x: 400, y: 400),
control1: CGPoint(x: 0, y: 400),
control2: CGPoint(x: 400, y: 0))
}
.stroke(Color(hex:"0093E9"), lineWidth: 10)
```
1. [addCurve(to:controlPoint1:controlPoint2:)](https://developer.apple.com/documentation/uikit/uibezierpath/1624357-addcurve) Appends a cubic Bézier curve to the path.

### 使用 GeometryReader 動態調整長寬
目前都是 hardcore 定義圖形的位置,要根據 superview 來計算長度,就要使用 GeometryReader,`geometry` 型態是 `GeometryProxy`,可以取得 superview 的長寬。
```swift=
GeometryReader { geometry in
let width = geometry.size.width
let height = geometry.size.height
Path { path in
path.move(to: CGPoint(x: width * 0.1, y: height * 0.1))
path.addQuadCurve(to: CGPoint(x: width * 0.9, y: height * 0.9), control: CGPoint(x: width * 0.1, y: height * 0.9))
}
.stroke(Color(hex:"0093E9"), lineWidth: 10)
}
```
1. [GeometryReader](https://developer.apple.com/documentation/swiftui/geometryreader) A container view that defines its content as a function of its own size and coordinate space.
2. [GeometryProxy](https://developer.apple.com/documentation/swiftui/geometryproxy) A proxy for access to the size and coordinate space (for anchor resolution) of the container view.

### 旋轉畫面
搭配 `overlay` 看出選擇不同的 `anchor` 變化。
```swift=
Rectangle()
.frame(width: 100, height: 200)
.foregroundColor(.gray)
.overlay {
Rectangle()
.frame(width: 100, height: 200)
.foregroundColor(gradientStart)
.rotationEffect(Angle(degrees: 40), anchor: .bottom)
}
```
1. [rotationEffect](https://developer.apple.com/documentation/swiftui/view/rotationeffect(_:anchor:)) - Rotates this view’s rendered output around the specified point.

## Animating Views and Transitions
[🔗 Apple Tutorial](https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions)
### 使用 animation() 元件顯示動畫
當`animation()`的`value`改變時,會驅動Animation
```swift=
Label("Graph", systemImage: "chevron.right.circle")
.labelStyle(.iconOnly)
.imageScale(.large)
.rotationEffect(.degrees(showDetail ? 90 : 0))
.scaleEffect(showDetail ? 1.5 : 1)
.padding()
.animation(.spring(), value: showDetail)
```
1. [Label](https://developer.apple.com/documentation/swiftui/label) - A standard label for user interface items, consisting of an icon with a title.
Availability
3. [scaleEffect](https://developer.apple.com/documentation/swiftui/text/scaleeffect(_:anchor:)-9qu74) - Scales this view’s rendered output by the given amount in both the horizontal and vertical directions, relative to an anchor point.
4. [animation](https://developer.apple.com/documentation/swiftui/view/animation(_:value:)) - Applies the given animation to this view when the specified value changes.
Animation有很多種,可以都玩玩看效果,想知道有哪些可以查看 [Animation](https://developer.apple.com/documentation/swiftui/animation),`spring(response:dampingFraction:blendDuration)`滿有趣的,會有彈簧效果。
```swift=
.animation(.easeInOut, value: value)
.animation(.linear, value: value)
.animation(.spring(), value: value)
```
調整動畫執行時間
```swift=
.animation(.easeInOut(duration: 3), value: value)
```
重複次數,autoreverses 會帶著動畫回到開始,反之,會直接回到開始的狀態
```swift=
.animation(.easeInOut.repeatCount(5, autoreverses: true), value: value)
.animation(.easeInOut.repeatForever(autoreverses: true), value: value)
```
### 使用 withAnimation { } 顯示動畫
為顯示 HikeDetail 和 Label 加上特效,以例子為例,會有淡入淡出效果,另外,Label 因為有額外設定特效,會以該設定為準。
```swift=
Button {
withAnimation(.easeInOut) {
showDetail.toggle()
}
} label: {
Label("Graph", systemImage: "chevron.right.circle")
.labelStyle(.iconOnly)
.imageScale(.large)
.rotationEffect(.degrees(showDetail ? 90 : 0))
.scaleEffect(showDetail ? 1.5 : 1)
.padding()
.animation(.easeInOut.repeatForever(autoreverses: true), value: showDetail)
}
if showDetail {
HikeDetail(hike: hike)
}
```
### Customize Animation
透過 extension Animation 可以定義客製的動畫,官方範例中,帶入 index 製造不同的延遲效果,讓長條圖有波動的效果。
```swift=
extension Animation {
static func ripple(index: Int) -> Animation {
Animation.spring(dampingFraction: 0.5)
.speed(2)
.delay(0.03 * Double(index))
}
}
```
### Customize View Transitions
上面提到 HikeDetail 的轉場特效預設是淡入淡出,可以透過 `.transition` 設定,[AnyTransition](https://developer.apple.com/documentation/swiftui/anytransition) 有幾種選擇,像是 `opacity`, `scale`, `slide`, `move(edge:)`, `identity`
```swift=
HikeDetail(hike: hike)
.transition(.slide)
```
[combined(with: AnyTransition)](https://developer.apple.com/documentation/swiftui/anytransition/combined(with:)) 可以把轉場動畫串接起來。
```swift=
HikeDetail(hike: hike)
.transition(.scale
.combined(with: .opacity)
.combined(with: .move(edge: .trailing)))
```
[asymmetric(insertion:removal:)](https://developer.apple.com/documentation/swiftui/anytransition/asymmetric(insertion:removal:)) 可以分別控制進場和出場的特效,比如,下方客製了一種轉場特效叫做 moveAndFade 和其用法。
```swift=
extension AnyTransition {
static var moveAndFade: AnyTransition {
AnyTransition.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .scale.combined(with: .opacity)
)
}
}
```
```swift=
HikeDetail(hike: hike)
.transition(.moveAndFade)
```
## 畫畫時間 🎨
1. 可以先照著 Apple Tutorial 做一次
2. 過程中,可以嘗試不同的動畫、轉場特效
3. 如果已經會的人,也可以嘗試畫不同的圖形

[source code](https://gist.github.com/bing-Guo/4ba7681c35e40027a466f41c01976ac3)