-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSpringAnimationsView.swift
157 lines (135 loc) · 4.29 KB
/
SpringAnimationsView.swift
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//
// SpringAnimationsView.swift
// FluidInterfacesSwiftUI
//
// Created by Frad LEE on 5/31/21.
//
import SwiftUI
// MARK: - SpringAnimationsView
/// A simple spring animation demo.
///
/// # Key Features
///
/// 1. Uses SwiftUI `spring()` animation.
/// 2. No concept of animation duration in single `spring()`.
/// 3. Easily interruptible.
///
/// # Design Theory
///
/// Springs make great animation models because of their speed and natural appearance. A spring
/// animation starts incredibly quickly, spending most of its time gradually approaching its final
/// state.
///
/// This is perfect for creating interfaces that feel responsive—they spring to life!
///
/// - Note:
/// A persistent spring animation. When mixed with other `spring()` or
/// `interactiveSpring()` animations on the same property, each animation will be replaced
/// by their successor, preserving velocity from one animation to the next. Optionally blends the
/// response values between springs over a time period.
///
/// # References
///
/// - [Building Fluid Interfaces. How to create natural gestures and…](https://medium.com/@nathangitter/building-fluid-interfaces-ios-swift-9732bb934bf5)
/// - [SOLVED/ Instantly reset state of animation. ](https://www.hackingwithswift.com/forums/swiftui/instantly-reset-state-of-animation/4494 )
/// - [swiftui - Spring Animation/ What does the blendDuration parameter do?](https://stackoverflow.com/a/59170144 )
struct SpringAnimationsView: View {
// MARK: Internal
var body: some View {
VStack(spacing: 128) {
ResetRoundedRectangle(
response: $response,
dampingFraction: $dampingFraction
)
.id(viewID)
VStack {
HeaderView(title: "Response (Speed)", number: $response)
Slider(
value: $response,
in: 0 ... 1.0,
onEditingChanged: { _ in
sliderChanged()
}
)
HeaderView(title: "Damping Fraction", number: $dampingFraction)
Slider(
value: $dampingFraction,
in: 0 ... 1.0,
onEditingChanged: { _ in
sliderChanged()
}
)
}
}
.padding()
}
// MARK: Private
private struct HeaderView: View {
@State var title: String
@Binding var number: Double
var body: some View {
HStack {
Text(title)
.textCase(.uppercase)
Spacer()
Text("\(number, specifier: "%.2f")")
.font(.system(.body).monospacedDigit())
}
}
}
@State private var isAnimated = false
@State private var response = 0.55
@State private var dampingFraction = 0.825
@State private var blendDuration = 0.0
@State private var viewID = 0
private func sliderChanged() {
viewID += 1
}
}
// MARK: - ResetRoundedRectangle
private struct ResetRoundedRectangle: View {
// MARK: Internal
@Binding var response: Double
@Binding var dampingFraction: Double
var body: some View {
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 32.0)
.fill(
LinearGradient(
gradient: Gradient(colors: [.topColor, .bottomColor]),
startPoint: .top,
endPoint: .bottom
)
)
.frame(width: 120, height: 120, alignment: .center)
.offset(x: isAnimated ? 0 : geometry.size.width - 120,
y: geometry.size.height / 2)
.onAppear {
withAnimation(Animation.spring(
response: response,
dampingFraction: dampingFraction,
/// In single `spring()` animation, it is no need to use `blendDuration`.
/// If you want to konw how `blendDuration` works, please see
/// `SpringBlendDuration` from [Mark Moeykens](https://stackoverflow.com/a/59170144 ).
blendDuration: 0.0
)
.repeatForever(autoreverses: false)) {
isAnimated = true
}
}
}
}
// MARK: Private
@State private var isAnimated = false
}
/// Colors of rounded rectangle.
private extension Color {
static let topColor = Color(red: 0.39, green: 0.80, blue: 0.97)
static let bottomColor = Color(red: 0.21, green: 0.62, blue: 0.93)
}
// MARK: - SpringAnimationsView_Previews
struct SpringAnimationsView_Previews: PreviewProvider {
static var previews: some View {
SpringAnimationsView()
}
}