做二手房又做網(wǎng)站的如何制作一個網(wǎng)頁
概述
在 SwiftUI 中,我們可以借助漸變色(Gradient)來實現(xiàn)更加靈動多彩的著色效果。從 SwiftUI 6.0 開始,蘋果增加了全新的網(wǎng)格漸變色讓我們對其有了更自由的定制度。
因為 gif 格式圖片自身的顯示能力有限,所以上面的動圖無法傳神的還原實際的美妙效果。強烈建議大家在模擬器或真機上運行本文中的示例代碼。
在本篇博文中,您將學到如下內(nèi)容:
- 概述
- 1. 漸變色的前世今生
- 2. 動畫加持,美輪美奐
- 3. 綜合運用
- 總結
閑言少敘,讓我們馬上進入漸變色的世界吧!
Let‘s dive in!!!😉
1. 漸變色的前世今生
在 SwiftUI 中小伙伴們時常會用漸變色(或稱為階梯色)來裝扮我們的界面。
在 SwiftUI 1.0(iOS 13)中有 3 種漸變色類型,它們分別是:線性漸變色 LinearGradient、輻射漸變色 RadialGradient、以及角度漸變色 AngularGradient。
關于使用它們對進行任意視圖裁剪的進一步介紹,請小伙伴們移步如下鏈接觀賞精彩的內(nèi)容:
- SwiftUI用Gradient顏色裁剪任意視圖
而在 SwiftUI 3.0(iOS 15)中,蘋果又添加了一款橢圓漸變色 EllipticalGradient:
為了能夠更加輕松的使用單一顏色的漸變色,蘋果從 SwiftUI 4.0(iOS 16)開始干脆將其直接“融入”到 Color 的實例中去了:
我們可以這樣使用它:
Text("Hello Panda").foregroundStyle(.red.gradient)
在 WWDC 24 中,蘋果再接再厲為 SwiftUI 6.0(iOS 18)添加了全新漸變色風格:網(wǎng)格漸變色(MeshGradient ):
別被它的名字所嚇到,其實它只是用縱橫交錯的方格來進一步細粒度控制顏色漸變的自由度,僅此而已。
使用網(wǎng)格漸變色很簡單,我們只需創(chuàng)建一個 MeshGradient 實例即可:
MeshGradient(width: 2,height: 2,points: [.init(x: 0, y: 0),.init(x: 1, y: 0), .init(x: 0, y: 1), .init(x: 1, y: 1)],colors: [.red, .green, .blue, .yellow])
如上代碼所示:我們創(chuàng)建了一個 2 x 2 網(wǎng)格漸變色,并將其左上角、右上角、左下角、右下角的顏色依次設置為紅色、綠色、藍色以及黃色:
現(xiàn)在我們“靜如處子”的網(wǎng)格漸變色貌似略顯“呆滯”。別急,通過適當?shù)卣{(diào)整其內(nèi)部各個網(wǎng)格邊框的基準點(或者顏色),我們可以讓它行云流水般的“動如脫兔”。
2. 動畫加持,美輪美奐
上面說過,要想動畫網(wǎng)格漸變色很簡單。我們只需使用若干狀態(tài)來實時的描述 MeshGradient 中每個網(wǎng)格邊框的相對位置以及網(wǎng)格內(nèi)的顏色即可。
首先,我們創(chuàng)建一個 positions 數(shù)組來表示每個網(wǎng)格的邊框,注意這是一個 3 x 3 網(wǎng)格:
@State var positions: [SIMD2<Float>] = [.init(x: 0, y: 0), .init(x: 0.2, y: 0), .init(x: 1, y: 0),.init(x: 0, y: 0.7), .init(x: 0.1, y: 0.5), .init(x: 1, y: 0.2),.init(x: 0, y: 1), .init(x: 0.9, y: 1), .init(x: 1, y: 1)]
接下來,我們利用定時器連續(xù)調(diào)整 positions 里所有非頂角網(wǎng)格邊框的相對位置。排除頂角網(wǎng)格的原因是:我們不想讓整個網(wǎng)格漸變色在頂點被裁剪:
具體實現(xiàn)代碼如下所示:
let timer = Timer.publish(every: 1/9, on: .current, in: .common).autoconnect()let colors: [Color] = [.purple, .red, .yellow,.blue, .green, .orange,.indigo, .teal, .cyan
]func randomizePosition(currentPosition: SIMD2<Float>,xRange: (min: Float, max: Float),yRange: (min: Float, max: Float)
) -> SIMD2<Float> {let updateDistance: Float = 0.01let newX = if Bool.random() {min(currentPosition.x + updateDistance, xRange.max)} else {max(currentPosition.x - updateDistance, xRange.min)}let newY = if Bool.random() {min(currentPosition.y + updateDistance, yRange.max)} else {max(currentPosition.y - updateDistance, yRange.min)}return .init(x: newX, y: newY)
}MeshGradient(width: 3,height: 3,points: positions,colors: colors).animation(.bouncy, value: positions).onReceive(timer, perform: { _ inpositions[1] = randomizePosition(currentPosition: positions[1],xRange: (min: 0.2, max: 0.9),yRange: (min: 0, max: 0))positions[3] = randomizePosition(currentPosition: positions[3],xRange: (min: 0, max: 0),yRange: (min: 0.2, max: 0.8))positions[4] = randomizePosition(currentPosition: positions[4],xRange: (min: 0.3, max: 0.8),yRange: (min: 0.3, max: 0.8))positions[5] = randomizePosition(currentPosition: positions[5],xRange: (min: 1, max: 1),yRange: (min: 0.1, max: 0.9))positions[7] = randomizePosition(currentPosition: positions[7],xRange: (min: 0.1, max: 0.9),yRange: (min: 1, max: 1))}).animation(.bouncy, value: positions).ignoresSafeArea()
編譯并在 Xcode 預覽中運行一見分曉:
再次重申:上面動圖“顆粒感”很強是因為 gif 圖片本身對顏色限制(最多顯示 256 種顏色)的原因,實際效果會相當絲滑順暢。
現(xiàn)在,我們不但可以恣意描繪靜態(tài)漸變色,利用些許動畫我們還可以讓它活靈活現(xiàn)的呈現(xiàn)效果“秾姿故薰欲醉眼,芳信暗傳嘗苦心”。棒棒噠!💯
想要系統(tǒng)學習最新 Swift 語言如何美美噠的進行蘋果開發(fā)的小伙伴們,可以到我的《Swift語言開發(fā)精講》專欄來逛一逛哦:
- Swift 語言開發(fā)精講
3. 綜合運用
下面是一個將網(wǎng)格漸變色溶入到我們實際應用中的演示代碼。在代碼中我們做了這樣幾件事:
- 用不同狀態(tài)控制不同的動畫效果
- 使用 mask 將網(wǎng)格漸變色嵌入到文本視圖中
- 擴展 View 以實現(xiàn)更簡潔的視圖方法
全部源代碼在此:
import SwiftUIextension View {@ViewBuilderfunc scaleEffect(_ ratio: CGFloat) -> some View {scaleEffect(x: ratio, y: ratio)}
}struct ContentView: View {@State var bgAnimStart = false@State var shadowAnimStart = false@State var positions: [SIMD2<Float>] = [.init(x: 0, y: 0), .init(x: 0.2, y: 0), .init(x: 1, y: 0),.init(x: 0, y: 0.7), .init(x: 0.1, y: 0.5), .init(x: 1, y: 0.2),.init(x: 0, y: 1), .init(x: 0.9, y: 1), .init(x: 1, y: 1)]let timer = Timer.publish(every: 1/9, on: .current, in: .common).autoconnect()let colors1: [Color] = [.purple, .red, .yellow,.blue, .green, .orange,.indigo, .teal, .cyan]let colors2: [Color] = [.black, .red, .blue,.black, .teal, .blue,.blue, .red, .black]func randomizePosition(currentPosition: SIMD2<Float>,xRange: (min: Float, max: Float),yRange: (min: Float, max: Float)) -> SIMD2<Float> {let updateDistance: Float = 0.01let newX = if Bool.random() {min(currentPosition.x + updateDistance, xRange.max)} else {max(currentPosition.x - updateDistance, xRange.min)}let newY = if Bool.random() {min(currentPosition.y + updateDistance, yRange.max)} else {max(currentPosition.y - updateDistance, yRange.min)}return .init(x: newX, y: newY)}func createMeshGradientView(_ colors: [Color]) -> some View {MeshGradient(width: 3,height: 3,points: positions,colors: colors).animation(.bouncy, value: positions).onReceive(timer, perform: { _ inpositions[1] = randomizePosition(currentPosition: positions[1],xRange: (min: 0.2, max: 0.9),yRange: (min: 0, max: 0))positions[3] = randomizePosition(currentPosition: positions[3],xRange: (min: 0, max: 0),yRange: (min: 0.2, max: 0.8))positions[4] = randomizePosition(currentPosition: positions[4],xRange: (min: 0.3, max: 0.8),yRange: (min: 0.3, max: 0.8))positions[5] = randomizePosition(currentPosition: positions[5],xRange: (min: 1, max: 1),yRange: (min: 0.1, max: 0.9))positions[7] = randomizePosition(currentPosition: positions[7],xRange: (min: 0.1, max: 0.9),yRange: (min: 1, max: 1))})}let text = Text("Hello Panda").font(.system(size: 108, weight: .heavy, design: .rounded)).foregroundStyle(.red.gradient)var body: some View {NavigationStack {ZStack {createMeshGradientView(colors1)//.blur(radius: 30.0).opacity(0.8)text.frame(maxWidth: .infinity, maxHeight: .infinity).opacity(0.01).background {createMeshGradientView(colors2).mask {text.scaleEffect(bgAnimStart ? 1.1 : 1.0).rotationEffect(.degrees(bgAnimStart ? -10 : 0))}.shadow(color: shadowAnimStart ? .green : .black, radius: 10)}}.ignoresSafeArea().navigationTitle("Mesh Gradient 演示").toolbar {Text("大熊貓侯佩 @ \(Text("CSDN").foregroundStyle(.red))").foregroundStyle(.primary.secondary).font(.headline)}}.task {withAnimation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true)) {shadowAnimStart = true}withAnimation(.snappy(duration: 0.66, extraBounce: 15.0).repeatForever(autoreverses: true)) {bgAnimStart = true}}}
}#Preview {ContentView()
}
總結
在本篇博文中,我們討論了 SwiftUI 6.0(iOS 18)中全新網(wǎng)格漸變色 MeshGradient 的使用,并隨后介紹如何利用酷炫的動畫升華它的動態(tài)效果。
感謝觀看,再會啦!😎