一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Swift - SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

2021-01-15 16:26CodingSuccess Swift

這篇文章主要介紹了SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧

老鐵們,是時候燥起來了!本文中我們將學習如何使用 SwiftUI 中的 PathsAnimatableData 來制作顏色切換動畫

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫
 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

 

這些快速切換的動畫是怎么實現的呢?讓我們來看下文吧!

基礎

要實現動畫的關鍵是在 SwiftUI 中創建一個實現 Shape 協議的結構體。我們把它命名為 SplashShape 。在 Shape 協議中,有一個方法叫做 path(in rect: CGRect) -> Path ,這個方法可以用來設置圖形的外觀。我們就用這個方法來實現本文中的各種動畫。

創建 SplashShape 結構體

下面我們創建一個叫做 SplashStruct 的結構體,它繼承于 Shape 協議。

?
1
2
3
4
5
6
7
8
import SwiftUI
 
struct SplashShape: Shape {
 
 func path(in rect: CGRect) -> Path {
 return Path()
 }
}

我們首先創建兩種動畫類型: leftToRightrightToLeft ,效果如下所示:

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫 

 

Splash 動畫

我們創建一個名為 SplashAnimation枚舉 來定義動畫類型,便于以后更方便地擴展新動畫(文章末尾可以驗證!)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
import SwiftUI
 
struct SplashShape: Shape {
 
 public enum SplashAnimation {
 case leftToRight
 case rightToleft
 }
 
 func path(in rect: CGRect) -> Path {
 return Path()
 }
}

path() 方法中,我們可以選擇需要使用的動畫,并且返回動畫的 Path 。但是首先,我們必須創建變量來存儲動畫類型,記錄動畫過程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SwiftUI
 
struct SplashShape: Shape {
 
 public enum SplashAnimation {
 case leftToRight
 case rightToleft
 }
 
 var progress: CGFloat
 var animationType: SplashAnimation
 
 func path(in rect: CGRect) -> Path {
 return Path()
 }
}

progress 的取值范圍在 0 和 1 之間,它代表整個動畫的完成進度。當我們編寫 path() 方法時,它就會派上用場。

編寫 path() 方法

跟之前說的一樣,為了返回正確的 Path ,我們需要明確正在使用哪一種動畫。在 path() 方法中編寫 switch 語句,并且用上我們之前定義的 animationType 。

?
1
2
3
4
5
6
7
8
func path(in rect: CGRect) -> Path {
 switch animationType {
 case .leftToRight:
  return Path()
 case .rightToLeft:
  return Path()
 }
}

現在這個方法只會返回空 paths。我們需要創建產生真實動畫的方法。

實現動畫方法

在 path() 方法的下面,創建兩個新的方法: leftToRight() 和 rightToLeft() ,每個方法表示一種動畫類型。在每個方法體內,我們會創建一個矩形形狀的 Path ,它會根據 progress 變量的值隨時間發生變換。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func leftToRight(rect: CGRect) -> Path {
 var path = Path()
 path.move(to: CGPoint(x: 0, y: 0)) // Top Left
 path.addLine(to: CGPoint(x: rect.width * progress, y: 0)) // Top Right
 path.addLine(to: CGPoint(x: rect.width * progress, y: rect.height)) // Bottom Right
 path.addLine(to: CGPoint(x: 0, y: rect.height)) // Bottom Left
 path.closeSubpath() // Close the Path
 return path
}
 
func rightToLeft(rect: CGRect) -> Path {
 var path = Path()
 path.move(to: CGPoint(x: rect.width, y: 0))
 path.addLine(to: CGPoint(x: rect.width - (rect.width * progress), y: 0))
 path.addLine(to: CGPoint(x: rect.width - (rect.width * progress), y: rect.height))
 path.addLine(to: CGPoint(x: rect.width, y: rect.height))
 path.closeSubpath()
 return path
}

然后在 path() 方法中調用上面兩個新方法。

?
1
2
3
4
5
6
7
8
func path(in rect: CGRect) -> Path {
 switch animationType {
 case .leftToRight:
  return leftToRight(rect: rect)
 case .rightToLeft:
  return rightToLeft(rect: rect)
 }
}

動畫數據

為了確保 Swift 知道在更改 progress 變量時如何對 Shape 進行動畫處理,我們需要指定一個響應動畫的變量。在 progress 和 animationType 變量下面,定義 animatableData 。這是一個基于 Animatable 協議 的變量,它可以通知 SwiftUI 在數據改變時,對視圖進行動畫處理。

?
1
2
3
4
5
6
7
var progress: CGFloat
var animationType: SplashAnimation
 
var animatableData: CGFloat {
 get { return progress }
 set { self.progress = newValue}
}
 

SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

顏色切換時產生動畫

到目前為止,我們已經創建了一個 Shape ,它將隨著時間的變化而變化。接下來,我們需要將它添加到視圖中,并在視圖顏色改變時自動對其進行動畫處理。這時候我們引入 SplashView 。我們將創建一個 SplashView 來自動更新 SplashShape 的 progress 變量。當 SplashView 接收到新的 Color 時,它將觸發動畫。

首先,我們創建 SplashView 結構體。

?
1
2
3
4
5
6
7
8
import SwiftUI
 
struct SplashView: View {
 
 var body: some View {
 // SplashShape Here
 }
}

SplashShape 需要使用 SplashAnimation 枚舉作為參數,所以我們會把它作為參數傳遞給 SplashView 。另外,我們要在視圖的背景顏色變化時設置動畫,所以我們也要傳遞 Color 參數。這些細節會在我們的初始化方法中詳細說明。

ColorStore 是自定義的 ObservableObject。它用來監聽 SplashView 結構體中 Color 值的改變,以便我們可以初始化 SplashShape 動畫,并最終改變背景顏色。我們稍后展示它的工作原理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct SplashView: View {
 
 var animationType: SplashShape.SplashAnimation
 @State private var prevColor: Color // Stores background color
 @ObservedObject var colorStore: ColorStore // Send new color updates
 
 
 init(animationType: SplashShape.SplashAnimation, color: Color) {
 self.animationType = animationType
 self._prevColor = State<Color>(initialValue: color)
 self.colorStore = ColorStore(color: color)
 }
 
 var body: some View {
 // SplashShape Here
 }
 
}
 
class ColorStore: ObservableObject {
 @Published var color: Color
 
 init(color: Color) {
 self.color = color
 }
}

構建 SplashView body

在 body 內部,我們需要返回一個 Rectangle ,它和 SplashView 當前的顏色保持一致。然后使用之前定義的 ColorStore ,以便于我們接收更新的顏色值來驅動動畫。

?
1
2
3
4
5
6
7
var body: some View {
 Rectangle()
 .foregroundColor(self.prevColor) // Current Color
 .onReceive(self.colorStore.$color) { color in
  // Animate Color Update Here
 }
}

當顏色改變時,我們需要記錄 SplashView 中正在改變的顏色和進度。為此,我們定義 layers 變量。

?
1
@State var layers: [(Color,CGFloat)] = [] // New Color & Progress

現在回到 body 變量內部,我們給 layers 變量添加新接收的 Colors 。添加的時候我們把進度設置為 0 。然后,在半秒之內的動畫過程中,我們把進度設置為 1 。

?
1
2
3
4
5
6
7
8
9
10
11
12
var body: some View {
 Rectangle()
 .foregroundColor(self.prevColor) // Current Color
 .onReceive(self.colorStore.$color) { color in
  // Animate Color Update Here
  self.layers.append((color, 0))
  
  withAnimation(.easeInOut(duration: 0.5)) {
  self.layers[self.layers.count-1].1 = 1.0
  }
 }
}

現在在這段代碼中, layers 變量中添加了更新后的顏色,但是顏色并沒有展示出來。為了展示顏色,我們需要在 body 變量內部為 Rectangle 的每一個圖層添加一個覆蓋層。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var body: some View {
 Rectangle()
 .foregroundColor(self.prevColor)
 .overlay(
  ZStack {
  ForEach(layers.indices, id: \.self) { x in
   SplashShape(progress: self.layers[x].1, animationType: self.animationType)
   .foregroundColor(self.layers[x].0)
  }
 
  }
 
  , alignment: .leading)
 .onReceive(self.colorStore.$color) { color in
  // Animate color update here
  self.layers.append((color, 0))
 
  withAnimation(.easeInOut(duration: 0.5)) {
  self.layers[self.layers.count-1].1 = 1.0
  }
 }
}

測試效果

你可以在模擬器中運行下面的代碼。這段代碼的意思是,當你點擊 ContentView 中的按鈕時,它會計算 index 來選擇 SplashView 中的顏色,同時也會觸發 ColorStore 內部的更新。所以,當 SplashShape 圖層添加到 SplashView 時,就會觸發動畫。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import SwiftUI
 
struct ContentView: View {
 var colors: [Color] = [.blue, .red, .green, .orange]
 @State var index: Int = 0
 
 @State var progress: CGFloat = 0
 var body: some View {
 VStack {
  
  SplashView(animationType: .leftToRight, color: self.colors[self.index])
  .frame(width: 200, height: 100, alignment: .center)
  .cornerRadius(10)
  .shadow(color: Color.black.opacity(0.2), radius: 10, x: 0, y: 4)
  
  Button(action: {
  self.index = (self.index + 1) % self.colors.count
  }) {
  Text("Change Color")
  }
  .padding(.top, 20)
 }
 
 }
}

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫 

 

還沒有完成!

我們還有一個功能沒實現。現在我們持續地把圖層添加到 SplashView 上,但是沒有刪除它們。因此,我們需要在動畫完成時把這些圖層清理掉。

在 SplashView 結構體 body 變量的 onReceive() 方法內部,做如下改變:

?
1
2
3
4
5
6
7
8
9
10
11
.onReceive(self.colorStore.$color) { color in
 self.layers.append((color, 0))
 
 withAnimation(.easeInOut(duration: 0.5)) {
 self.layers[self.layers.count-1].1 = 1.0
 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  self.prevColor = self.layers[0].0 // Finalizes background color of SplashView
  self.layers.remove(at: 0) // removes itself from layers array
 }
 }
}

這行代碼能讓我們刪除 layers 數組中使用過的值,并確保 SplashView 基于最新更新的值顯示正確的背景色。

展示成果!

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫 

 

GitHub 源碼

您可以在我的 Github 上查看本教程的源代碼 !除了顯示的示例外,還包括 SplashShape 和 SplashView 的完整源代碼。 ....但是等等,還有更多!

彩蛋!

如果你熟悉我之前的教程,你應該了解我喜歡彩蛋 :wink:。在本文開頭,我說過會實現更多動畫。此刻終于來了…… 擊鼓 ……。

Splash 動畫

哈哈哈!!還記得嗎?我說過會添加更多動畫種類。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum SplashAnimation {
 case leftToRight
 case rightToLeft
 case topToBottom
 case bottomToTop
 case angle(Angle)
 case circle
}
func path(in rect: CGRect) -> Path {
 
 switch self.animationType {
 case .leftToRight:
  return leftToRight(rect: rect)
 case .rightToLeft:
  return rightToLeft(rect: rect)
 case .topToBottom:
  return topToBottom(rect: rect)
 case .bottomToTop:
  return bottomToTop(rect: rect)
 case .angle(let splashAngle):
  return angle(rect: rect, angle: splashAngle)
 case .circle:
  return circle(rect: rect)
 }
 
}

你肯定會想…… “哇, 彩蛋也太多了……” 。不必苦惱。我們只需要在 SplashShape 的 path() 方法中添加幾個方法,就能搞定。

下面我們逐個動畫來搞定……

topToBottom 和 bottomToTop 動畫

這些方法與 leftToRight 和 rightToLeft 非常相似,它們從 shape 的底部或頂部開始創建 path ,并使用 progress 變量隨時間對其進行變換。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func topToBottom(rect: CGRect) -> Path {
 var path = Path()
 path.move(to: CGPoint(x: 0, y: 0))
 path.addLine(to: CGPoint(x: rect.width, y: 0))
 path.addLine(to: CGPoint(x: rect.width, y: rect.height * progress))
 path.addLine(to: CGPoint(x: 0, y: rect.height * progress))
 path.closeSubpath()
 return path
}
 
func bottomToTop(rect: CGRect) -> Path {
 var path = Path()
 path.move(to: CGPoint(x: 0, y: rect.height))
 path.addLine(to: CGPoint(x: rect.width, y: rect.height))
 path.addLine(to: CGPoint(x: rect.width, y: rect.height - (rect.height * progress)))
 path.addLine(to: CGPoint(x: 0, y: rect.height - (rect.height * progress)))
 path.closeSubpath()
 return path
}

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫 

 

circle 動畫

如果你還記得小學幾何知識,就應該了解勾股定理。 a^2 + b^2 = c^2

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

 

a 和 b 可以視為矩形的 高度 和 寬度 ,我們能夠根據它們求得 c ,即覆蓋整個矩形所需的圓的半徑。我們以此為基礎構建圓的 path,并使用 progress 變量隨時間對它進行變換。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
func circle(rect: CGRect) -> Path {
 let a: CGFloat = rect.height / 2.0
 let b: CGFloat = rect.width / 2.0
 
 let c = pow(pow(a, 2) + pow(b, 2), 0.5) // a^2 + b^2 = c^2 --> Solved for 'c'
 // c = radius of final circle
 
 let radius = c * progress
 // Build Circle Path
 var path = Path()
 path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: radius, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 360), clockwise: true)
 return path
}
 

SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

angle 動畫

這個動畫知識點有點多。你需要使用切線計算角度的斜率,然后根據這個斜率創建一條直線。在矩形上移動這條直線時,根據它來繪制一個直角三角形。參見下圖,各種彩色的線表示該線隨時間移動時,覆蓋整個矩形的狀態。

 

 
SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

 

方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func angle(rect: CGRect, angle: Angle) -> Path {
 
 var cAngle = Angle(degrees: angle.degrees.truncatingRemainder(dividingBy: 90))
 
 // Return Path Using Other Animations (topToBottom, leftToRight, etc) if angle is 0, 90, 180, 270
 if angle.degrees == 0 || cAngle.degrees == 0 { return leftToRight(rect: rect)}
 else if angle.degrees == 90 || cAngle.degrees == 90 { return topToBottom(rect: rect)}
 else if angle.degrees == 180 || cAngle.degrees == 180 { return rightToLeft(rect: rect)}
 else if angle.degrees == 270 || cAngle.degrees == 270 { return bottomToTop(rect: rect)}
 
 
 // Calculate Slope of Line and inverse slope
 let m = CGFloat(tan(cAngle.radians))
 let m_1 = pow(m, -1) * -1
 let h = rect.height
 let w = rect.width
 
 // tan (angle) = slope of line
 // y = mx + b ---> b = y - mx ~ 'b' = y intercept
 let b = h - (m_1 * w) // b = y - (m * x)
 
 // X and Y coordinate calculation
 var x = b * m * progress
 var y = b * progress
 
 // Triangle Offset Calculation
 let xOffset = (angle.degrees > 90 && angle.degrees < 270) ? rect.width : 0
 let yOffset = (angle.degrees > 180 && angle.degrees < 360) ? rect.height : 0
 
 // Modify which side the triangle is drawn from depending on the angle
 if angle.degrees > 90 && angle.degrees < 180 { x *= -1 }
 else if angle.degrees > 180 && angle.degrees < 270 { x *= -1; y *= -1 }
 else if angle.degrees > 270 && angle.degrees < 360 { y *= -1 }
 
 // Build Triangle Path
 var path = Path()
 path.move(to: CGPoint(x: xOffset, y: yOffset))
 path.addLine(to: CGPoint(x: xOffset + x, y: yOffset))
 path.addLine(to: CGPoint(x: xOffset, y: yOffset + y))
 path.closeSubpath()
 return path
 
}
 

SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫

總結

到此這篇關于SwiftUI使用Paths和AnimatableData實現酷炫的顏色切換動畫的文章就介紹到這了,更多相關SwiftUI 顏色切換動畫內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.im/post/5eb39472e51d454dae5575cd

延伸 · 閱讀

精彩推薦
  • SwiftSwift中轉義閉包示例詳解

    Swift中轉義閉包示例詳解

    在Swift 中的閉包類似于結構塊,并可以在任何地方調用,下面這篇文章主要給大家介紹了關于Swift中轉義閉包的相關資料,需要的朋友可以參考下...

    小小小_小朋友11412021-12-26
  • SwiftSwift使用CollectionView實現廣告欄滑動效果

    Swift使用CollectionView實現廣告欄滑動效果

    這篇文章主要為大家詳細介紹了Swift使用CollectionView實現廣告欄滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Stevin的技術博客12372021-01-13
  • SwiftSwift能代替Objective-C嗎?

    Swift能代替Objective-C嗎?

    這是我在網上上看到的答案,復制粘貼過來和大家分享一下,因為我和很多人一樣很關心Swift的出現對Mac開發的影響和對Objective-C的影響。...

    Swift教程網4412020-12-16
  • SwiftSwift實現多個TableView側滑與切換效果

    Swift實現多個TableView側滑與切換效果

    這篇文章主要為大家詳細介紹了Swift實現多個TableView側滑與切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    乞力馬扎羅的雪雪5822021-01-08
  • SwiftSwift的74個常用內置函數介紹

    Swift的74個常用內置函數介紹

    這篇文章主要介紹了Swift的74個常用內置函數介紹,這篇文章列舉出了所有的Swift庫函數,內置函數是指無需引入任何模塊即可以直接使用的函數,需要的朋友可...

    Swift教程網5802020-12-19
  • Swiftswift where與匹配模式的實例詳解

    swift where與匹配模式的實例詳解

    這篇文章主要介紹了swift where與匹配模式的實例詳解的相關資料,這里附有簡單的示例代碼,講的比較清楚,需要的朋友可以參考下...

    追到夢的魔術師14382021-01-06
  • Swiftmac git xcrun error active developer path 錯誤

    mac git xcrun error active developer path 錯誤

    本文主要是講訴了如何解決在mac下使用git;xcode4.6的環境時,出現了錯誤(mac git xcrun error active developer path)的解決辦法,希望對大家有所幫助...

    Swift教程網2232020-12-16
  • SwiftSwift教程之基礎數據類型詳解

    Swift教程之基礎數據類型詳解

    這篇文章主要介紹了Swift教程之基礎數據類型詳解,本文詳細講解了Swift中的基本數據類型和基本語法,例如常量和變量、注釋、分號、整數、數值類型轉換等...

    Swift教程網5162020-12-18
主站蜘蛛池模板: 国产高清在线不卡 | 逼中极品| 男老头澡堂gay老头456 | 小小水蜜桃免费影院 | 91久久精品国产一区二区 | 日本大片免a费观看在线 | 色噜噜国产精品视频一区二区 | 免费一级日本c片完整版 | 国产精品久久国产精品99 | 96萝莉| 国产成人免费高清激情明星 | 国产精品亚洲片夜色在线 | 国产亚洲欧美在线中文bt天堂网 | 亚洲一区二区三区福利在线 | 奇米影视777最新在线 | 嫩草影院永久入口在线观看 | xxy中国厕所 | 久久亚洲午夜牛牛影视 | 丝瓜茄子绿巨人秋葵榴莲污 | 天天噜| 5g影院天天影院天天爽影院网站 | 久久永久免费视频 | 粉嫩国产14xxxxx0000| 国产99热99| 美女脱了内裤打开腿让人桶网站o | 92国产福利视频一区二区 | yy6080午夜国产免费福利 | 性欧美金发洋妞xxxxbbbb | 亚洲乱码一二三四五六区 | 女同志 videos | 亚洲精品午夜级久久久久 | 91视频完整版 | zol中关村在线官网 yy6080欧美三级理论 | 桃色视频软件 | 日本色女 | 日本网络视频www色高清免费 | 九九热在线免费观看 | 国产亚洲精品综合在线网址 | 范冰冰性xxxxhd | 天天综合天天色 | 亚洲第一福利视频 |