react-native-smooth-clip-view 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -388
- package/android/build.gradle +3 -3
- package/android/proguard-rules.pro +2 -2
- package/android/src/main/cpp/SmoothClipAndroid.h +9 -12
- package/android/src/main/cpp/SmoothClipBindings.cpp +300 -704
- package/android/src/main/cpp/SmoothClipRegistry.cpp +348 -594
- package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +36 -227
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +16 -19
- package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +36 -294
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +344 -488
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +75 -77
- package/cpp/SmoothClipAnimationCurve.h +237 -346
- package/cpp/SmoothClipRegistry.h +24 -52
- package/cpp/SmoothClipSharedGeometry.h +64 -94
- package/cpp/SmoothClipVelocityTracker.h +5 -5
- package/ios/SmoothClipGeometry.h +23 -34
- package/ios/SmoothClipRegistry.mm +206 -711
- package/ios/SmoothClipTurboModule.cpp +217 -715
- package/ios/SmoothClipTurboModule.h +54 -297
- package/ios/SmoothClipView.mm +599 -505
- package/ios/SmoothClipViewRegistry.h +2 -7
- package/lib/module/NativeSmoothClipModule.js.map +1 -1
- package/lib/module/SmoothClipView.ios.js +84 -17
- package/lib/module/SmoothClipView.ios.js.map +1 -1
- package/lib/module/SmoothClipView.js +1 -1
- package/lib/module/SmoothClipView.js.map +1 -1
- package/lib/module/SmoothClipViewNativeComponent.ts +20 -26
- package/lib/module/capabilities.native.js +1 -19
- package/lib/module/capabilities.native.js.map +1 -1
- package/lib/module/capabilityTypes.js +0 -9
- package/lib/module/capabilityTypes.js.map +1 -1
- package/lib/module/controllerInternals.js +28 -0
- package/lib/module/controllerInternals.js.map +1 -0
- package/lib/module/controllerLifecycle.js +9 -0
- package/lib/module/controllerLifecycle.js.map +1 -0
- package/lib/module/controllerTypes.js +4 -0
- package/lib/module/controllerTypes.js.map +1 -0
- package/lib/module/controllers.android.js +4 -0
- package/lib/module/controllers.android.js.map +1 -0
- package/lib/module/controllers.ios.js +4 -0
- package/lib/module/controllers.ios.js.map +1 -0
- package/lib/module/controllers.js +4 -0
- package/lib/module/controllers.js.map +1 -0
- package/lib/module/controllers.native.js +89 -0
- package/lib/module/controllers.native.js.map +1 -0
- package/lib/module/geometry.js +71 -51
- package/lib/module/geometry.js.map +1 -1
- package/lib/module/groupTypes.js +4 -0
- package/lib/module/{groupDriverTypes.js.map → groupTypes.js.map} +1 -1
- package/lib/module/groups.android.js +4 -0
- package/lib/module/groups.android.js.map +1 -0
- package/lib/module/groups.ios.js +4 -0
- package/lib/module/groups.ios.js.map +1 -0
- package/lib/module/groups.js +4 -0
- package/lib/module/groups.js.map +1 -0
- package/lib/module/groups.native.js +399 -0
- package/lib/module/groups.native.js.map +1 -0
- package/lib/module/ids.js +8 -0
- package/lib/module/ids.js.map +1 -0
- package/lib/module/index.js +3 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/nativeCompletion.js +37 -16
- package/lib/module/nativeCompletion.js.map +1 -1
- package/lib/module/presentationCodec.js +81 -0
- package/lib/module/presentationCodec.js.map +1 -0
- package/lib/typescript/src/NativeSmoothClipModule.d.ts +14 -23
- package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.ios.d.ts +18 -14
- package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +10 -5
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/capabilities.native.d.ts +1 -1
- package/lib/typescript/src/capabilities.native.d.ts.map +1 -1
- package/lib/typescript/src/capabilityTypes.d.ts +0 -6
- package/lib/typescript/src/capabilityTypes.d.ts.map +1 -1
- package/lib/typescript/src/controllerInternals.d.ts +14 -0
- package/lib/typescript/src/controllerInternals.d.ts.map +1 -0
- package/lib/typescript/src/controllerLifecycle.d.ts +3 -0
- package/lib/typescript/src/controllerLifecycle.d.ts.map +1 -0
- package/lib/typescript/src/controllerTypes.d.ts +62 -0
- package/lib/typescript/src/controllerTypes.d.ts.map +1 -0
- package/lib/typescript/src/controllers.android.d.ts +2 -0
- package/lib/typescript/src/controllers.android.d.ts.map +1 -0
- package/lib/typescript/src/controllers.d.ts +2 -0
- package/lib/typescript/src/controllers.d.ts.map +1 -0
- package/lib/typescript/src/controllers.ios.d.ts +2 -0
- package/lib/typescript/src/controllers.ios.d.ts.map +1 -0
- package/lib/typescript/src/controllers.native.d.ts +3 -0
- package/lib/typescript/src/controllers.native.d.ts.map +1 -0
- package/lib/typescript/src/geometry.d.ts +22 -12
- package/lib/typescript/src/geometry.d.ts.map +1 -1
- package/lib/typescript/src/groupTypes.d.ts +31 -0
- package/lib/typescript/src/groupTypes.d.ts.map +1 -0
- package/lib/typescript/src/groups.android.d.ts +2 -0
- package/lib/typescript/src/groups.android.d.ts.map +1 -0
- package/lib/typescript/src/groups.d.ts +2 -0
- package/lib/typescript/src/groups.d.ts.map +1 -0
- package/lib/typescript/src/groups.ios.d.ts +2 -0
- package/lib/typescript/src/groups.ios.d.ts.map +1 -0
- package/lib/typescript/src/groups.native.d.ts +3 -0
- package/lib/typescript/src/groups.native.d.ts.map +1 -0
- package/lib/typescript/src/ids.d.ts +2 -0
- package/lib/typescript/src/ids.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -6
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/nativeCompletion.d.ts +16 -6
- package/lib/typescript/src/nativeCompletion.d.ts.map +1 -1
- package/lib/typescript/src/presentationCodec.d.ts +10 -0
- package/lib/typescript/src/presentationCodec.d.ts.map +1 -0
- package/package.json +8 -3
- package/src/NativeSmoothClipModule.ts +41 -292
- package/src/SmoothClipView.ios.tsx +130 -23
- package/src/SmoothClipView.tsx +5 -1
- package/src/SmoothClipViewNativeComponent.ts +20 -26
- package/src/capabilities.native.ts +2 -52
- package/src/capabilityTypes.ts +0 -14
- package/src/controllerInternals.ts +52 -0
- package/src/controllerLifecycle.ts +6 -0
- package/src/controllerTypes.ts +87 -0
- package/src/controllers.android.ts +1 -0
- package/src/controllers.ios.ts +1 -0
- package/src/controllers.native.ts +104 -0
- package/src/controllers.ts +1 -0
- package/src/geometry.ts +118 -74
- package/src/groupTypes.ts +53 -0
- package/src/groups.android.ts +1 -0
- package/src/groups.ios.ts +1 -0
- package/src/groups.native.ts +604 -0
- package/src/groups.ts +1 -0
- package/src/ids.ts +6 -0
- package/src/index.ts +20 -28
- package/src/nativeCompletion.ts +67 -32
- package/src/presentationCodec.ts +134 -0
- package/lib/module/driverState.js +0 -82
- package/lib/module/driverState.js.map +0 -1
- package/lib/module/driverTypes.js +0 -4
- package/lib/module/driverTypes.js.map +0 -1
- package/lib/module/drivers.android.js +0 -6
- package/lib/module/drivers.android.js.map +0 -1
- package/lib/module/drivers.ios.js +0 -6
- package/lib/module/drivers.ios.js.map +0 -1
- package/lib/module/drivers.js +0 -4
- package/lib/module/drivers.js.map +0 -1
- package/lib/module/drivers.native.js +0 -756
- package/lib/module/drivers.native.js.map +0 -1
- package/lib/module/groupDriverTypes.js +0 -4
- package/lib/module/groupDrivers.android.js +0 -4
- package/lib/module/groupDrivers.android.js.map +0 -1
- package/lib/module/groupDrivers.ios.js +0 -4
- package/lib/module/groupDrivers.ios.js.map +0 -1
- package/lib/module/groupDrivers.js +0 -4
- package/lib/module/groupDrivers.js.map +0 -1
- package/lib/module/groupDrivers.native.js +0 -799
- package/lib/module/groupDrivers.native.js.map +0 -1
- package/lib/module/presentationProtocol.js +0 -15
- package/lib/module/presentationProtocol.js.map +0 -1
- package/lib/module/reactRequests.js +0 -56
- package/lib/module/reactRequests.js.map +0 -1
- package/lib/typescript/src/driverState.d.ts +0 -26
- package/lib/typescript/src/driverState.d.ts.map +0 -1
- package/lib/typescript/src/driverTypes.d.ts +0 -121
- package/lib/typescript/src/driverTypes.d.ts.map +0 -1
- package/lib/typescript/src/drivers.android.d.ts +0 -3
- package/lib/typescript/src/drivers.android.d.ts.map +0 -1
- package/lib/typescript/src/drivers.d.ts +0 -3
- package/lib/typescript/src/drivers.d.ts.map +0 -1
- package/lib/typescript/src/drivers.ios.d.ts +0 -3
- package/lib/typescript/src/drivers.ios.d.ts.map +0 -1
- package/lib/typescript/src/drivers.native.d.ts +0 -5
- package/lib/typescript/src/drivers.native.d.ts.map +0 -1
- package/lib/typescript/src/groupDriverTypes.d.ts +0 -66
- package/lib/typescript/src/groupDriverTypes.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.android.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.android.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.ios.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.ios.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.native.d.ts +0 -4
- package/lib/typescript/src/groupDrivers.native.d.ts.map +0 -1
- package/lib/typescript/src/presentationProtocol.d.ts +0 -4
- package/lib/typescript/src/presentationProtocol.d.ts.map +0 -1
- package/lib/typescript/src/reactRequests.d.ts +0 -9
- package/lib/typescript/src/reactRequests.d.ts.map +0 -1
- package/src/driverState.ts +0 -118
- package/src/driverTypes.ts +0 -171
- package/src/drivers.android.ts +0 -8
- package/src/drivers.ios.ts +0 -8
- package/src/drivers.native.ts +0 -1599
- package/src/drivers.ts +0 -6
- package/src/groupDriverTypes.ts +0 -117
- package/src/groupDrivers.android.ts +0 -1
- package/src/groupDrivers.ios.ts +0 -1
- package/src/groupDrivers.native.ts +0 -1189
- package/src/groupDrivers.ts +0 -1
- package/src/presentationProtocol.ts +0 -28
- package/src/reactRequests.ts +0 -76
package/ios/SmoothClipView.mm
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
#import <react/renderer/components/SmoothClipViewSpec/Props.h>
|
|
17
17
|
#import <react/renderer/components/SmoothClipViewSpec/RCTComponentViewHelpers.h>
|
|
18
18
|
|
|
19
|
+
#include <algorithm>
|
|
19
20
|
#include <cfloat>
|
|
21
|
+
#include <cmath>
|
|
20
22
|
|
|
21
23
|
using namespace facebook::react;
|
|
22
24
|
|
|
@@ -80,7 +82,7 @@ static SmoothClipCornerCurve SmoothClipPresentationCurve(
|
|
|
80
82
|
: SmoothClipCornerCurveCircular;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
static bool
|
|
85
|
+
static bool SmoothClipBuildPresentation(
|
|
84
86
|
double x,
|
|
85
87
|
double y,
|
|
86
88
|
double width,
|
|
@@ -93,13 +95,30 @@ static bool SmoothClipBuildPresentationV2(
|
|
|
93
95
|
double contentTranslateX,
|
|
94
96
|
double contentTranslateY,
|
|
95
97
|
double contentScale,
|
|
98
|
+
BOOL shadowEnabled,
|
|
99
|
+
double shadowRed,
|
|
100
|
+
double shadowGreen,
|
|
101
|
+
double shadowBlue,
|
|
102
|
+
double shadowAlpha,
|
|
103
|
+
double shadowOffsetX,
|
|
104
|
+
double shadowOffsetY,
|
|
105
|
+
double shadowBlurRadius,
|
|
106
|
+
double shadowSpreadDistance,
|
|
96
107
|
smoothclip::Presentation *result) {
|
|
97
108
|
if (result == nullptr || !isfinite(x) || !isfinite(y) ||
|
|
98
109
|
!isfinite(width) || !isfinite(height) ||
|
|
99
110
|
!isfinite(topLeftRadius) || !isfinite(topRightRadius) ||
|
|
100
111
|
!isfinite(bottomRightRadius) || !isfinite(bottomLeftRadius) ||
|
|
101
112
|
!isfinite(contentTranslateX) || !isfinite(contentTranslateY) ||
|
|
102
|
-
!isfinite(contentScale) ||
|
|
113
|
+
!isfinite(contentScale) || !isfinite(shadowRed) ||
|
|
114
|
+
!isfinite(shadowGreen) || !isfinite(shadowBlue) ||
|
|
115
|
+
!isfinite(shadowAlpha) ||
|
|
116
|
+
!isfinite(shadowOffsetX) || !isfinite(shadowOffsetY) ||
|
|
117
|
+
!isfinite(shadowBlurRadius) || !isfinite(shadowSpreadDistance) ||
|
|
118
|
+
contentScale <= 0 || shadowRed < 0 || shadowRed > 1 ||
|
|
119
|
+
shadowGreen < 0 || shadowGreen > 1 ||
|
|
120
|
+
shadowBlue < 0 || shadowBlue > 1 ||
|
|
121
|
+
shadowAlpha < 0 || shadowAlpha > 1 || shadowBlurRadius < 0 ||
|
|
103
122
|
(curveCode != static_cast<NSInteger>(smoothclip::ClipCurve::Circular) &&
|
|
104
123
|
curveCode != static_cast<NSInteger>(smoothclip::ClipCurve::Continuous))) {
|
|
105
124
|
return false;
|
|
@@ -115,11 +134,121 @@ static bool SmoothClipBuildPresentationV2(
|
|
|
115
134
|
geometry.bottomRightRadius = bottomRightRadius;
|
|
116
135
|
geometry.bottomLeftRadius = bottomLeftRadius;
|
|
117
136
|
geometry.curve = static_cast<smoothclip::ClipCurve>(curveCode);
|
|
137
|
+
const smoothclip::Shadow shadow{
|
|
138
|
+
shadowEnabled,
|
|
139
|
+
shadowRed,
|
|
140
|
+
shadowGreen,
|
|
141
|
+
shadowBlue,
|
|
142
|
+
shadowAlpha,
|
|
143
|
+
shadowOffsetX,
|
|
144
|
+
shadowOffsetY,
|
|
145
|
+
shadowBlurRadius,
|
|
146
|
+
shadowSpreadDistance};
|
|
118
147
|
*result = {
|
|
119
|
-
geometry, contentTranslateX, contentTranslateY, contentScale};
|
|
148
|
+
geometry, contentTranslateX, contentTranslateY, contentScale, shadow};
|
|
120
149
|
return true;
|
|
121
150
|
}
|
|
122
151
|
|
|
152
|
+
static bool SmoothBoxShadowEqual(
|
|
153
|
+
const smoothclip::Shadow &first,
|
|
154
|
+
const smoothclip::Shadow &second) {
|
|
155
|
+
return first.enabled == second.enabled &&
|
|
156
|
+
first.red == second.red && first.green == second.green &&
|
|
157
|
+
first.blue == second.blue && first.alpha == second.alpha &&
|
|
158
|
+
first.offsetX == second.offsetX && first.offsetY == second.offsetY &&
|
|
159
|
+
first.blurRadius == second.blurRadius &&
|
|
160
|
+
first.spreadDistance == second.spreadDistance;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static bool SmoothBoxShadowVisible(
|
|
164
|
+
SmoothClipCanonicalGeometry geometry,
|
|
165
|
+
const smoothclip::Shadow &shadow) {
|
|
166
|
+
return shadow.enabled && shadow.alpha > 0 && !CGRectIsEmpty(geometry.rect);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static smoothclip::Shadow SmoothBoxShadowWithEffectiveAlpha(
|
|
170
|
+
SmoothClipCanonicalGeometry geometry,
|
|
171
|
+
smoothclip::Shadow shadow) {
|
|
172
|
+
if (!SmoothBoxShadowVisible(geometry, shadow)) {
|
|
173
|
+
shadow.alpha = 0;
|
|
174
|
+
}
|
|
175
|
+
return shadow;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static void SmoothBoxShadowNormalizeAnimationEndpoints(
|
|
179
|
+
SmoothClipCanonicalGeometry fromGeometry,
|
|
180
|
+
smoothclip::Shadow *fromShadow,
|
|
181
|
+
SmoothClipCanonicalGeometry toGeometry,
|
|
182
|
+
smoothclip::Shadow *toShadow) {
|
|
183
|
+
const bool fromVisible = SmoothBoxShadowVisible(fromGeometry, *fromShadow);
|
|
184
|
+
const bool toVisible = SmoothBoxShadowVisible(toGeometry, *toShadow);
|
|
185
|
+
if (fromVisible == toVisible) {
|
|
186
|
+
*fromShadow = SmoothBoxShadowWithEffectiveAlpha(
|
|
187
|
+
fromGeometry, *fromShadow);
|
|
188
|
+
*toShadow = SmoothBoxShadowWithEffectiveAlpha(toGeometry, *toShadow);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
smoothclip::Shadow *invisible = fromVisible ? toShadow : fromShadow;
|
|
193
|
+
const smoothclip::Shadow &visible = fromVisible ? *fromShadow : *toShadow;
|
|
194
|
+
invisible->enabled = true;
|
|
195
|
+
invisible->red = visible.red;
|
|
196
|
+
invisible->green = visible.green;
|
|
197
|
+
invisible->blue = visible.blue;
|
|
198
|
+
invisible->alpha = 0;
|
|
199
|
+
invisible->offsetX = visible.offsetX;
|
|
200
|
+
invisible->offsetY = visible.offsetY;
|
|
201
|
+
invisible->blurRadius = visible.blurRadius;
|
|
202
|
+
invisible->spreadDistance = visible.spreadDistance;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static bool SmoothBoxShadowColorEqual(
|
|
206
|
+
const smoothclip::Shadow &first,
|
|
207
|
+
const smoothclip::Shadow &second) {
|
|
208
|
+
return first.red == second.red && first.green == second.green &&
|
|
209
|
+
first.blue == second.blue && first.alpha == second.alpha;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static bool SmoothBoxShadowPathInputEqual(
|
|
213
|
+
SmoothClipCanonicalGeometry firstGeometry,
|
|
214
|
+
const smoothclip::Shadow &firstShadow,
|
|
215
|
+
SmoothClipCanonicalGeometry secondGeometry,
|
|
216
|
+
const smoothclip::Shadow &secondShadow) {
|
|
217
|
+
return CGRectEqualToRect(firstGeometry.rect, secondGeometry.rect) &&
|
|
218
|
+
SmoothClipCornerRadiiEqual(
|
|
219
|
+
firstGeometry.radii, secondGeometry.radii) &&
|
|
220
|
+
firstGeometry.curve == secondGeometry.curve &&
|
|
221
|
+
firstShadow.spreadDistance == secondShadow.spreadDistance;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
static CGFloat SmoothClipAdjustedShadowRadius(
|
|
225
|
+
CGFloat radius,
|
|
226
|
+
CGFloat spread) {
|
|
227
|
+
const CGFloat magnitude = ABS(spread);
|
|
228
|
+
CGFloat multiplier = 1;
|
|
229
|
+
if (magnitude > 0 && radius < magnitude) {
|
|
230
|
+
multiplier = 1 + pow(radius / magnitude - 1, 3);
|
|
231
|
+
}
|
|
232
|
+
return MAX(0, radius + spread * multiplier);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static CGPathRef SmoothClipCreateShadowPath(
|
|
236
|
+
SmoothClipCanonicalGeometry geometry,
|
|
237
|
+
const smoothclip::Shadow &shadow) {
|
|
238
|
+
const CGFloat spread = shadow.spreadDistance;
|
|
239
|
+
CGRect rect = CGRectInset(geometry.rect, -spread, -spread);
|
|
240
|
+
if (CGRectGetWidth(rect) <= 0 || CGRectGetHeight(rect) <= 0) {
|
|
241
|
+
rect = CGRectZero;
|
|
242
|
+
}
|
|
243
|
+
const SmoothClipCornerRadii radii = {
|
|
244
|
+
SmoothClipAdjustedShadowRadius(geometry.radii.topLeft, spread),
|
|
245
|
+
SmoothClipAdjustedShadowRadius(geometry.radii.topRight, spread),
|
|
246
|
+
SmoothClipAdjustedShadowRadius(geometry.radii.bottomRight, spread),
|
|
247
|
+
SmoothClipAdjustedShadowRadius(geometry.radii.bottomLeft, spread),
|
|
248
|
+
};
|
|
249
|
+
return SmoothClipCreateRoundedRectPath(rect, radii, geometry.curve);
|
|
250
|
+
}
|
|
251
|
+
|
|
123
252
|
static NSString *SmoothClipCALayerCornerCurve(
|
|
124
253
|
SmoothClipCornerCurve curve) API_AVAILABLE(ios(13.0)) {
|
|
125
254
|
return curve == SmoothClipCornerCurveContinuous
|
|
@@ -191,24 +320,38 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
191
320
|
contentScale};
|
|
192
321
|
}
|
|
193
322
|
|
|
323
|
+
static BOOL SmoothClipRectIntersectsHost(CGRect rect, CGRect host) {
|
|
324
|
+
const CGRect intersection = CGRectIntersection(rect, host);
|
|
325
|
+
return !CGRectIsNull(intersection) && !CGRectIsEmpty(intersection);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static smoothclip::Presentation SmoothClipCanonicalSnapshot(
|
|
329
|
+
smoothclip::Presentation presentation) {
|
|
330
|
+
smoothclip::canonicalizePresentation(presentation);
|
|
331
|
+
return presentation;
|
|
332
|
+
}
|
|
333
|
+
|
|
194
334
|
@implementation SmoothClipView {
|
|
335
|
+
CALayer *_shadowLayer;
|
|
195
336
|
SmoothClipContainerView *_clipContainer;
|
|
196
337
|
SmoothClipContainerView *_contentContainer;
|
|
197
338
|
CAShapeLayer *_unequalCornerMask;
|
|
198
339
|
CGRect _requestedClip;
|
|
199
|
-
CGRect
|
|
340
|
+
CGRect _canonicalClip;
|
|
200
341
|
CGFloat _requestedRadius;
|
|
201
|
-
CGFloat
|
|
342
|
+
CGFloat _canonicalRadius;
|
|
202
343
|
SmoothClipCornerRadii _requestedRadii;
|
|
203
|
-
SmoothClipCornerRadii
|
|
344
|
+
SmoothClipCornerRadii _canonicalRadii;
|
|
204
345
|
SmoothClipCornerCurve _requestedCurve;
|
|
205
|
-
SmoothClipCornerCurve
|
|
346
|
+
SmoothClipCornerCurve _canonicalCurve;
|
|
206
347
|
BOOL _requestedHasExplicitRadii;
|
|
207
|
-
BOOL
|
|
348
|
+
BOOL _canonicalHasExplicitRadii;
|
|
208
349
|
CGPoint _requestedContentTranslation;
|
|
209
|
-
CGPoint
|
|
350
|
+
CGPoint _canonicalContentTranslation;
|
|
210
351
|
CGFloat _requestedContentScale;
|
|
211
|
-
CGFloat
|
|
352
|
+
CGFloat _canonicalContentScale;
|
|
353
|
+
smoothclip::Shadow _requestedShadow;
|
|
354
|
+
smoothclip::Shadow _canonicalShadow;
|
|
212
355
|
uint64_t _driverId;
|
|
213
356
|
BOOL _hasLayout;
|
|
214
357
|
BOOL _commandIsAuthoritative;
|
|
@@ -219,23 +362,11 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
219
362
|
|
|
220
363
|
int32_t _activeAnimationId;
|
|
221
364
|
NSInteger _activeAnimationKind;
|
|
222
|
-
// Set when a native animation install arrived before the first layout.
|
|
223
|
-
// The install is deferred and re-run through the registry at first layout
|
|
224
|
-
// so it joins from live presentation geometry with the true remaining time.
|
|
225
|
-
BOOL _pendingAnimationInstall;
|
|
226
|
-
CFTimeInterval _animationStartedAt;
|
|
227
|
-
CFTimeInterval _animationDuration;
|
|
228
|
-
// Bumped on every CA install. updateLayoutMetrics captures it on entry so a
|
|
229
|
-
// registry-driven install that happens inside its own lifecycle notification
|
|
230
|
-
// (a latch resume during that very layout pass) is detectable: the local
|
|
231
|
-
// rebuild must then be skipped, because its captured remaining time predates
|
|
232
|
-
// the suspension and would shorten — or zero out — the fresh install.
|
|
233
|
-
uint32_t _animationInstallGeneration;
|
|
234
365
|
smoothclip::TimingAnimation _timingAnimation;
|
|
235
366
|
smoothclip::SpringAnimation _springAnimation;
|
|
236
367
|
SmoothClipAnimationDelegate *_animationDelegate;
|
|
237
368
|
|
|
238
|
-
// 'inherit' velocity samples (
|
|
369
|
+
// 'inherit' velocity samples (raw canonical geometry, per view); recording/
|
|
239
370
|
// coalescing/projection live in the shared cpp/SmoothClipVelocityTracker.h
|
|
240
371
|
// (behavior-paired with Android's per-driver history).
|
|
241
372
|
smoothclip::VelocitySampleHistory _velocitySamples;
|
|
@@ -262,6 +393,9 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
262
393
|
static const auto defaultProps =
|
|
263
394
|
std::make_shared<const SmoothClipViewProps>();
|
|
264
395
|
_props = defaultProps;
|
|
396
|
+
// SmoothClipView is the fixed viewport. Consumer overflow props are
|
|
397
|
+
// intentionally overridden again after every prop update below.
|
|
398
|
+
self.clipsToBounds = YES;
|
|
265
399
|
|
|
266
400
|
_clipContainer = [[SmoothClipContainerView alloc] initWithFrame:CGRectZero];
|
|
267
401
|
_clipContainer.layer.masksToBounds = YES;
|
|
@@ -277,6 +411,9 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
277
411
|
[_clipContainer addSubview:_contentContainer];
|
|
278
412
|
[self addSubview:_clipContainer];
|
|
279
413
|
|
|
414
|
+
_shadowLayer = nil;
|
|
415
|
+
// Created lazily so the common clipping-only path owns no shadow layer.
|
|
416
|
+
|
|
280
417
|
_unequalCornerMask = [CAShapeLayer layer];
|
|
281
418
|
_unequalCornerMask.actions = @{
|
|
282
419
|
@"path": [NSNull null],
|
|
@@ -287,27 +424,27 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
287
424
|
_unequalCornerMask.needsDisplayOnBoundsChange = NO;
|
|
288
425
|
|
|
289
426
|
_requestedClip = CGRectZero;
|
|
290
|
-
|
|
427
|
+
_canonicalClip = CGRectZero;
|
|
291
428
|
_requestedRadius = 0;
|
|
292
|
-
|
|
429
|
+
_canonicalRadius = 0;
|
|
293
430
|
_requestedRadii = {0, 0, 0, 0};
|
|
294
|
-
|
|
431
|
+
_canonicalRadii = {0, 0, 0, 0};
|
|
295
432
|
_requestedCurve = SmoothClipCornerCurveCircular;
|
|
296
|
-
|
|
433
|
+
_canonicalCurve = SmoothClipCornerCurveCircular;
|
|
297
434
|
_requestedHasExplicitRadii = NO;
|
|
298
|
-
|
|
435
|
+
_canonicalHasExplicitRadii = NO;
|
|
299
436
|
_requestedContentTranslation = CGPointZero;
|
|
300
|
-
|
|
437
|
+
_canonicalContentTranslation = CGPointZero;
|
|
301
438
|
_requestedContentScale = 1;
|
|
302
|
-
|
|
439
|
+
_canonicalContentScale = 1;
|
|
440
|
+
_requestedShadow = {};
|
|
441
|
+
_canonicalShadow = {};
|
|
303
442
|
_driverId = 0;
|
|
304
443
|
_hasLayout = NO;
|
|
305
444
|
_commandIsAuthoritative = NO;
|
|
306
445
|
_ignoreAnimationCallback = NO;
|
|
307
446
|
_activeAnimationId = 0;
|
|
308
447
|
_activeAnimationKind = 0;
|
|
309
|
-
_pendingAnimationInstall = NO;
|
|
310
|
-
_animationInstallGeneration = 0;
|
|
311
448
|
smoothclip::clearVelocitySamples(_velocitySamples);
|
|
312
449
|
// App-state transitions are observed by the registry itself (see
|
|
313
450
|
// installApplicationStateObservers): the active flag is process-global,
|
|
@@ -364,9 +501,61 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
364
501
|
presentation.contentTranslateX,
|
|
365
502
|
presentation.contentTranslateY);
|
|
366
503
|
_requestedContentScale = presentation.contentScale;
|
|
504
|
+
_requestedShadow = presentation.shadow;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
- (CGColorRef)colorForShadow:(const smoothclip::Shadow &)shadow {
|
|
508
|
+
return [UIColor colorWithRed:smoothclip::clamp01(shadow.red)
|
|
509
|
+
green:smoothclip::clamp01(shadow.green)
|
|
510
|
+
blue:smoothclip::clamp01(shadow.blue)
|
|
511
|
+
alpha:smoothclip::clamp01(shadow.alpha)].CGColor;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
- (CALayer *)ensureShadowLayer {
|
|
515
|
+
if (_shadowLayer != nil) return _shadowLayer;
|
|
516
|
+
_shadowLayer = [CALayer layer];
|
|
517
|
+
_shadowLayer.masksToBounds = NO;
|
|
518
|
+
_shadowLayer.actions = @{
|
|
519
|
+
@"shadowPath": [NSNull null],
|
|
520
|
+
@"shadowColor": [NSNull null],
|
|
521
|
+
@"shadowOpacity": [NSNull null],
|
|
522
|
+
@"shadowRadius": [NSNull null],
|
|
523
|
+
@"shadowOffset": [NSNull null],
|
|
524
|
+
@"bounds": [NSNull null],
|
|
525
|
+
@"position": [NSNull null],
|
|
526
|
+
};
|
|
527
|
+
[self.layer insertSublayer:_shadowLayer below:_clipContainer.layer];
|
|
528
|
+
return _shadowLayer;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
- (void)writeShadowModel:(const smoothclip::Shadow &)shadow
|
|
532
|
+
geometry:(SmoothClipCanonicalGeometry)geometry {
|
|
533
|
+
const smoothclip::Shadow effectiveShadow =
|
|
534
|
+
SmoothBoxShadowWithEffectiveAlpha(geometry, shadow);
|
|
535
|
+
const BOOL visible = SmoothBoxShadowVisible(geometry, effectiveShadow);
|
|
536
|
+
if (_shadowLayer == nil && !visible) return;
|
|
537
|
+
|
|
538
|
+
CALayer *shadowLayer = visible ? [self ensureShadowLayer] : _shadowLayer;
|
|
539
|
+
shadowLayer.bounds = self.bounds;
|
|
540
|
+
shadowLayer.position = CGPointMake(
|
|
541
|
+
CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
|
|
542
|
+
CGPathRef path = SmoothClipCreateShadowPath(geometry, effectiveShadow);
|
|
543
|
+
shadowLayer.shadowPath = path;
|
|
544
|
+
CGPathRelease(path);
|
|
545
|
+
shadowLayer.shadowColor = [self colorForShadow:effectiveShadow];
|
|
546
|
+
shadowLayer.shadowOpacity = visible ? 1 : 0;
|
|
547
|
+
shadowLayer.shadowRadius = MAX(0, effectiveShadow.blurRadius) / 2.0;
|
|
548
|
+
shadowLayer.shadowOffset = CGSizeMake(
|
|
549
|
+
effectiveShadow.offsetX, effectiveShadow.offsetY);
|
|
367
550
|
}
|
|
368
551
|
|
|
369
|
-
- (
|
|
552
|
+
- (void)writeShadow:(const smoothclip::Shadow &)shadow
|
|
553
|
+
geometry:(SmoothClipCanonicalGeometry)geometry {
|
|
554
|
+
_canonicalShadow = shadow;
|
|
555
|
+
[self writeShadowModel:shadow geometry:geometry];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
- (BOOL)canonicalRequestedGeometry:(SmoothClipCanonicalGeometry *)geometry {
|
|
370
559
|
if (!_hasLayout) return NO;
|
|
371
560
|
#if defined(SMOOTH_CLIP_ENABLE_SIGNPOSTS) && SMOOTH_CLIP_ENABLE_SIGNPOSTS
|
|
372
561
|
const bool signpostsEnabled =
|
|
@@ -375,11 +564,10 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
375
564
|
if (signpostsEnabled) {
|
|
376
565
|
identifier = os_signpost_id_generate([SmoothClipView signpostLog]);
|
|
377
566
|
os_signpost_interval_begin(
|
|
378
|
-
[SmoothClipView signpostLog], identifier, "
|
|
567
|
+
[SmoothClipView signpostLog], identifier, "canonicalization");
|
|
379
568
|
}
|
|
380
569
|
#endif
|
|
381
|
-
const
|
|
382
|
-
const BOOL valid = SmoothClipNormalizeGeometryV2(
|
|
570
|
+
const BOOL valid = SmoothClipCanonicalizeGeometry(
|
|
383
571
|
CGRectGetMinX(_requestedClip),
|
|
384
572
|
CGRectGetMinY(_requestedClip),
|
|
385
573
|
CGRectGetWidth(_requestedClip),
|
|
@@ -389,12 +577,11 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
389
577
|
_requestedRadii.bottomRight,
|
|
390
578
|
_requestedRadii.bottomLeft,
|
|
391
579
|
_requestedCurve,
|
|
392
|
-
CGSizeMake(MAX(0, hostSize.width), MAX(0, hostSize.height)),
|
|
393
580
|
geometry);
|
|
394
581
|
#if defined(SMOOTH_CLIP_ENABLE_SIGNPOSTS) && SMOOTH_CLIP_ENABLE_SIGNPOSTS
|
|
395
582
|
if (signpostsEnabled) {
|
|
396
583
|
os_signpost_interval_end(
|
|
397
|
-
[SmoothClipView signpostLog], identifier, "
|
|
584
|
+
[SmoothClipView signpostLog], identifier, "canonicalization");
|
|
398
585
|
}
|
|
399
586
|
#endif
|
|
400
587
|
return valid;
|
|
@@ -409,15 +596,15 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
409
596
|
|
|
410
597
|
- (void)syncVisibilityForRect:(CGRect)rect {
|
|
411
598
|
[self setClipContainerHidden:CGRectIsEmpty(rect)];
|
|
599
|
+
_clipContainer.accessibilityElementsHidden =
|
|
600
|
+
!SmoothClipRectIntersectsHost(rect, self.bounds);
|
|
412
601
|
}
|
|
413
602
|
|
|
414
603
|
- (void)configureUnequalCornerMaskForGeometry:
|
|
415
|
-
(
|
|
416
|
-
|
|
417
|
-
_unequalCornerMask.bounds = CGRectMake(
|
|
418
|
-
0, 0, MAX(0, hostSize.width), MAX(0, hostSize.height));
|
|
604
|
+
(SmoothClipCanonicalGeometry)geometry {
|
|
605
|
+
_unequalCornerMask.bounds = geometry.rect;
|
|
419
606
|
_unequalCornerMask.position = CGPointMake(
|
|
420
|
-
|
|
607
|
+
CGRectGetMidX(geometry.rect), CGRectGetMidY(geometry.rect));
|
|
421
608
|
CGPathRef path = SmoothClipCreateRoundedRectPath(
|
|
422
609
|
geometry.rect, geometry.radii, geometry.curve);
|
|
423
610
|
_unequalCornerMask.path = path;
|
|
@@ -425,7 +612,7 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
425
612
|
}
|
|
426
613
|
|
|
427
614
|
- (void)applyStaticCornerRepresentation:
|
|
428
|
-
(
|
|
615
|
+
(SmoothClipCanonicalGeometry)geometry {
|
|
429
616
|
CALayer *layer = _clipContainer.layer;
|
|
430
617
|
if (SmoothClipCornerRadiiAreUniform(geometry.radii)) {
|
|
431
618
|
layer.mask = nil;
|
|
@@ -439,29 +626,29 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
439
626
|
}
|
|
440
627
|
}
|
|
441
628
|
|
|
442
|
-
- (
|
|
629
|
+
- (SmoothClipCanonicalGeometry)canonicalGeometryValue {
|
|
443
630
|
return {
|
|
444
|
-
.rect =
|
|
445
|
-
.radius =
|
|
446
|
-
.radii =
|
|
447
|
-
.curve =
|
|
631
|
+
.rect = _canonicalClip,
|
|
632
|
+
.radius = _canonicalRadius,
|
|
633
|
+
.radii = _canonicalRadii,
|
|
634
|
+
.curve = _canonicalCurve,
|
|
448
635
|
};
|
|
449
636
|
}
|
|
450
637
|
|
|
451
|
-
- (void)writeLayerGeometry:(
|
|
638
|
+
- (void)writeLayerGeometry:(SmoothClipCanonicalGeometry)geometry
|
|
452
639
|
hasExplicitRadii:(BOOL)hasExplicitRadii {
|
|
453
640
|
const BOOL rectChanged =
|
|
454
|
-
!CGRectEqualToRect(
|
|
641
|
+
!CGRectEqualToRect(_canonicalClip, geometry.rect);
|
|
455
642
|
const BOOL radiiChanged =
|
|
456
|
-
!SmoothClipCornerRadiiEqual(
|
|
457
|
-
const BOOL curveChanged =
|
|
643
|
+
!SmoothClipCornerRadiiEqual(_canonicalRadii, geometry.radii);
|
|
644
|
+
const BOOL curveChanged = _canonicalCurve != geometry.curve;
|
|
458
645
|
const BOOL representationChanged =
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
646
|
+
_canonicalHasExplicitRadii != hasExplicitRadii;
|
|
647
|
+
_canonicalClip = geometry.rect;
|
|
648
|
+
_canonicalRadius = geometry.radius;
|
|
649
|
+
_canonicalRadii = geometry.radii;
|
|
650
|
+
_canonicalCurve = geometry.curve;
|
|
651
|
+
_canonicalHasExplicitRadii = hasExplicitRadii;
|
|
465
652
|
if (!rectChanged && !radiiChanged && !curveChanged &&
|
|
466
653
|
!representationChanged) {
|
|
467
654
|
return;
|
|
@@ -496,28 +683,33 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
496
683
|
|
|
497
684
|
- (void)writeContentTranslation:(CGPoint)translation
|
|
498
685
|
scale:(CGFloat)scale {
|
|
499
|
-
if (CGPointEqualToPoint(
|
|
500
|
-
|
|
686
|
+
if (CGPointEqualToPoint(_canonicalContentTranslation, translation) &&
|
|
687
|
+
_canonicalContentScale == scale) {
|
|
501
688
|
return;
|
|
502
689
|
}
|
|
503
|
-
|
|
504
|
-
|
|
690
|
+
_canonicalContentTranslation = translation;
|
|
691
|
+
_canonicalContentScale = scale;
|
|
505
692
|
_contentContainer.layer.affineTransform =
|
|
506
693
|
SmoothClipContentTransform(scale, translation);
|
|
507
694
|
}
|
|
508
695
|
|
|
509
696
|
- (void)applyRequestedClip {
|
|
510
|
-
|
|
511
|
-
if (![self
|
|
512
|
-
|
|
513
|
-
const BOOL
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
697
|
+
SmoothClipCanonicalGeometry geometry;
|
|
698
|
+
if (![self canonicalRequestedGeometry:&geometry]) return;
|
|
699
|
+
|
|
700
|
+
const BOOL accessibilityHidden =
|
|
701
|
+
!SmoothClipRectIntersectsHost(geometry.rect, self.bounds);
|
|
702
|
+
const BOOL visibilityChanged =
|
|
703
|
+
_clipHidden != CGRectIsEmpty(geometry.rect) ||
|
|
704
|
+
_clipContainer.accessibilityElementsHidden != accessibilityHidden;
|
|
705
|
+
if (CGRectEqualToRect(_canonicalClip, geometry.rect) &&
|
|
706
|
+
SmoothClipCornerRadiiEqual(_canonicalRadii, geometry.radii) &&
|
|
707
|
+
_canonicalCurve == geometry.curve &&
|
|
708
|
+
_canonicalHasExplicitRadii == _requestedHasExplicitRadii &&
|
|
518
709
|
CGPointEqualToPoint(
|
|
519
|
-
|
|
520
|
-
|
|
710
|
+
_canonicalContentTranslation, _requestedContentTranslation) &&
|
|
711
|
+
_canonicalContentScale == _requestedContentScale &&
|
|
712
|
+
SmoothBoxShadowEqual(_canonicalShadow, _requestedShadow) &&
|
|
521
713
|
!visibilityChanged) {
|
|
522
714
|
return;
|
|
523
715
|
}
|
|
@@ -525,6 +717,7 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
525
717
|
hasExplicitRadii:_requestedHasExplicitRadii];
|
|
526
718
|
[self writeContentTranslation:_requestedContentTranslation
|
|
527
719
|
scale:_requestedContentScale];
|
|
720
|
+
[self writeShadow:_requestedShadow geometry:geometry];
|
|
528
721
|
[self syncVisibilityForRect:geometry.rect];
|
|
529
722
|
}
|
|
530
723
|
|
|
@@ -545,17 +738,44 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
545
738
|
|
|
546
739
|
- (CGFloat)inheritedVelocityToRect:(CGRect)target
|
|
547
740
|
radii:(SmoothClipCornerRadii)targetRadii
|
|
548
|
-
contentTranslation:(CGPoint)targetContentTranslation
|
|
741
|
+
contentTranslation:(CGPoint)targetContentTranslation
|
|
742
|
+
contentScale:(CGFloat)targetContentScale {
|
|
549
743
|
return smoothclip::inheritedVelocity(
|
|
550
744
|
_velocitySamples,
|
|
551
745
|
SmoothClipVelocityChannels(
|
|
552
746
|
target,
|
|
553
747
|
targetRadii,
|
|
554
748
|
targetContentTranslation,
|
|
555
|
-
|
|
749
|
+
targetContentScale),
|
|
556
750
|
CACurrentMediaTime());
|
|
557
751
|
}
|
|
558
752
|
|
|
753
|
+
- (double)smoothClipInheritedVelocityToPresentation:
|
|
754
|
+
(smoothclip::Presentation)presentation {
|
|
755
|
+
SmoothClipCanonicalGeometry target;
|
|
756
|
+
const SmoothClipCornerRadii radii =
|
|
757
|
+
SmoothClipPresentationRadii(presentation.clip);
|
|
758
|
+
if (!SmoothClipCanonicalizeGeometry(
|
|
759
|
+
presentation.clip.x,
|
|
760
|
+
presentation.clip.y,
|
|
761
|
+
presentation.clip.width,
|
|
762
|
+
presentation.clip.height,
|
|
763
|
+
radii.topLeft,
|
|
764
|
+
radii.topRight,
|
|
765
|
+
radii.bottomRight,
|
|
766
|
+
radii.bottomLeft,
|
|
767
|
+
SmoothClipPresentationCurve(presentation.clip),
|
|
768
|
+
&target)) {
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
return [self inheritedVelocityToRect:target.rect
|
|
772
|
+
radii:target.radii
|
|
773
|
+
contentTranslation:CGPointMake(
|
|
774
|
+
presentation.contentTranslateX,
|
|
775
|
+
presentation.contentTranslateY)
|
|
776
|
+
contentScale:presentation.contentScale];
|
|
777
|
+
}
|
|
778
|
+
|
|
559
779
|
- (void)smoothClipApplyPresentation:(smoothclip::Presentation)presentation {
|
|
560
780
|
[self smoothClipApplyPresentation:presentation recordVelocitySample:YES];
|
|
561
781
|
}
|
|
@@ -565,21 +785,21 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
565
785
|
[self stopLayerAnimationWithoutCallback];
|
|
566
786
|
[self storeRequestedPresentation:presentation];
|
|
567
787
|
[self applyRequestedClip];
|
|
568
|
-
// Skipped for writes that are not interactive motion (a
|
|
788
|
+
// Skipped for writes that are not interactive motion (a pending-run
|
|
569
789
|
// cancel-to-target): they must not enter the 'inherit' history.
|
|
570
790
|
if (recordVelocitySample && _hasLayout) {
|
|
571
|
-
[self recordInteractiveRect:
|
|
572
|
-
radii:
|
|
573
|
-
contentTranslation:
|
|
574
|
-
contentScale:
|
|
791
|
+
[self recordInteractiveRect:_canonicalClip
|
|
792
|
+
radii:_canonicalRadii
|
|
793
|
+
contentTranslation:_canonicalContentTranslation
|
|
794
|
+
contentScale:_canonicalContentScale];
|
|
575
795
|
}
|
|
576
796
|
}
|
|
577
797
|
|
|
578
798
|
- (smoothclip::Presentation)smoothClipCurrentPresentation {
|
|
579
|
-
SmoothClipCornerRadii visibleRadii =
|
|
799
|
+
SmoothClipCornerRadii visibleRadii = _canonicalRadii;
|
|
580
800
|
const CGRect visibleRect = _activeAnimationId != 0
|
|
581
801
|
? [self presentationRectWithRadii:&visibleRadii]
|
|
582
|
-
:
|
|
802
|
+
: _canonicalClip;
|
|
583
803
|
CALayer *contentLayer = _activeAnimationId != 0
|
|
584
804
|
? (CALayer *)_contentContainer.layer.presentationLayer
|
|
585
805
|
: _contentContainer.layer;
|
|
@@ -592,17 +812,45 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
592
812
|
CGRectGetWidth(visibleRect),
|
|
593
813
|
CGRectGetHeight(visibleRect),
|
|
594
814
|
uniform ? visibleRadii.topLeft : 0};
|
|
595
|
-
if (
|
|
815
|
+
if (_canonicalHasExplicitRadii || !uniform) {
|
|
596
816
|
geometry.topLeftRadius = visibleRadii.topLeft;
|
|
597
817
|
geometry.topRightRadius = visibleRadii.topRight;
|
|
598
818
|
geometry.bottomRightRadius = visibleRadii.bottomRight;
|
|
599
819
|
geometry.bottomLeftRadius = visibleRadii.bottomLeft;
|
|
600
820
|
}
|
|
601
|
-
geometry.curve =
|
|
821
|
+
geometry.curve = _canonicalCurve == SmoothClipCornerCurveContinuous
|
|
602
822
|
? smoothclip::ClipCurve::Continuous
|
|
603
823
|
: smoothclip::ClipCurve::Circular;
|
|
604
824
|
const CGFloat contentScale = hypot(transform.a, transform.b);
|
|
605
|
-
|
|
825
|
+
smoothclip::Shadow shadow = _canonicalShadow;
|
|
826
|
+
CALayer *shadowLayer = _activeAnimationId != 0 && _shadowLayer != nil
|
|
827
|
+
? (CALayer *)_shadowLayer.presentationLayer
|
|
828
|
+
: _shadowLayer;
|
|
829
|
+
if (shadowLayer == nil) return SmoothClipCanonicalSnapshot({
|
|
830
|
+
geometry, transform.tx, transform.ty, contentScale, shadow});
|
|
831
|
+
shadow.enabled = _canonicalShadow.enabled || shadowLayer.shadowOpacity > 0;
|
|
832
|
+
shadow.blurRadius = shadowLayer.shadowRadius * 2.0;
|
|
833
|
+
shadow.offsetX = shadowLayer.shadowOffset.width;
|
|
834
|
+
shadow.offsetY = shadowLayer.shadowOffset.height;
|
|
835
|
+
CGColorRef color = shadowLayer.shadowColor;
|
|
836
|
+
if (color != nil) {
|
|
837
|
+
UIColor *uiColor = [UIColor colorWithCGColor:color];
|
|
838
|
+
CGFloat red = 0, green = 0, blue = 0, alpha = 0;
|
|
839
|
+
if ([uiColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
|
|
840
|
+
shadow.red = red;
|
|
841
|
+
shadow.green = green;
|
|
842
|
+
shadow.blue = blue;
|
|
843
|
+
shadow.alpha = alpha;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
CGPathRef shadowPath = shadowLayer.shadowPath;
|
|
847
|
+
if (shadowPath != nil && !CGRectIsEmpty(visibleRect)) {
|
|
848
|
+
const CGRect shadowBounds = CGPathGetBoundingBox(shadowPath);
|
|
849
|
+
shadow.spreadDistance =
|
|
850
|
+
CGRectGetMinX(visibleRect) - CGRectGetMinX(shadowBounds);
|
|
851
|
+
}
|
|
852
|
+
return SmoothClipCanonicalSnapshot(
|
|
853
|
+
{geometry, transform.tx, transform.ty, contentScale, shadow});
|
|
606
854
|
}
|
|
607
855
|
|
|
608
856
|
- (BOOL)smoothClipIsJoinable {
|
|
@@ -615,28 +863,15 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
615
863
|
- (BOOL)smoothClipCanDisplay {
|
|
616
864
|
// A CA animation committed while this view's layer tree is detached from
|
|
617
865
|
// the render tree does not survive the later attach commit; installs and
|
|
618
|
-
//
|
|
866
|
+
// pre-ready runs must wait until a frame can actually be produced.
|
|
619
867
|
return [self smoothClipIsJoinable] && self.window != nil &&
|
|
620
868
|
smoothclip::applicationIsActive();
|
|
621
869
|
}
|
|
622
870
|
|
|
623
|
-
- (BOOL)smoothClipHasPendingInstall {
|
|
624
|
-
return _pendingAnimationInstall;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
871
|
- (void)smoothClipClearVelocitySamples {
|
|
628
872
|
smoothclip::clearVelocitySamples(_velocitySamples);
|
|
629
873
|
}
|
|
630
874
|
|
|
631
|
-
- (double)smoothClipSpringContinuationVelocity {
|
|
632
|
-
if (_activeAnimationId == 0 || _activeAnimationKind != 2 ||
|
|
633
|
-
_pendingAnimationInstall) {
|
|
634
|
-
return 0;
|
|
635
|
-
}
|
|
636
|
-
const double elapsed = MAX(0, CACurrentMediaTime() - _animationStartedAt);
|
|
637
|
-
return smoothclip::springContinuationVelocity(_springAnimation, elapsed);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
875
|
- (smoothclip::Presentation)smoothClipFreezePresentation {
|
|
641
876
|
const smoothclip::Presentation visible =
|
|
642
877
|
[self smoothClipCurrentPresentation];
|
|
@@ -662,7 +897,7 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
662
897
|
(CAShapeLayer *)_unequalCornerMask.presentationLayer;
|
|
663
898
|
if (mask == nil) mask = _unequalCornerMask;
|
|
664
899
|
*radii = SmoothClipRadiiFromFixedPath(
|
|
665
|
-
mask.path, rect,
|
|
900
|
+
mask.path, rect, _canonicalRadii);
|
|
666
901
|
} else {
|
|
667
902
|
*radii = {
|
|
668
903
|
layer.cornerRadius,
|
|
@@ -676,7 +911,6 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
676
911
|
}
|
|
677
912
|
|
|
678
913
|
- (void)stopLayerAnimationWithoutCallback {
|
|
679
|
-
_pendingAnimationInstall = NO;
|
|
680
914
|
if (_activeAnimationId == 0) return;
|
|
681
915
|
// Invalidate before removing: CA may deliver didStop for the removed
|
|
682
916
|
// animation after this method returns, when _ignoreAnimationCallback has
|
|
@@ -687,7 +921,8 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
687
921
|
[_clipContainer.layer removeAnimationForKey:@"smoothClip.geometry"];
|
|
688
922
|
[_contentContainer.layer removeAnimationForKey:@"smoothClip.content"];
|
|
689
923
|
[_unequalCornerMask removeAnimationForKey:@"smoothClip.mask"];
|
|
690
|
-
[
|
|
924
|
+
[_shadowLayer removeAnimationForKey:@"smoothClip.shadow"];
|
|
925
|
+
[self applyStaticCornerRepresentation:[self canonicalGeometryValue]];
|
|
691
926
|
_ignoreAnimationCallback = NO;
|
|
692
927
|
_activeAnimationId = 0;
|
|
693
928
|
_activeAnimationKind = 0;
|
|
@@ -706,15 +941,19 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
706
941
|
}
|
|
707
942
|
|
|
708
943
|
- (CFTimeInterval)springSettlingDurationWithVelocity:(double)velocity {
|
|
709
|
-
// Every key path
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
944
|
+
// Every key path shares one normalized trajectory. Derive the terminal
|
|
945
|
+
// duration from the same relative-energy rule as Android/Reanimated instead
|
|
946
|
+
// of asking each Core Animation property for an independent settle time.
|
|
947
|
+
smoothclip::ScalarSpringState state{0, velocity};
|
|
948
|
+
constexpr double step = 1.0 / 120.0;
|
|
949
|
+
double elapsed = 0;
|
|
950
|
+
while (elapsed < 10.0 &&
|
|
951
|
+
smoothclip::relativeSpringEnergy(state, _springAnimation) >
|
|
952
|
+
_springAnimation.energyThreshold) {
|
|
953
|
+
state = smoothclip::advanceScalarSpring(state, _springAnimation, step);
|
|
954
|
+
elapsed += step;
|
|
955
|
+
}
|
|
956
|
+
return elapsed;
|
|
718
957
|
}
|
|
719
958
|
|
|
720
959
|
- (CASpringAnimation *)springAnimationForKeyPath:(NSString *)keyPath
|
|
@@ -735,14 +974,16 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
735
974
|
}
|
|
736
975
|
|
|
737
976
|
- (void)installAnimationFromGeometry:
|
|
738
|
-
(
|
|
977
|
+
(SmoothClipCanonicalGeometry)fromGeometry
|
|
739
978
|
fromContentTranslation:(CGPoint)fromContentTranslation
|
|
740
979
|
fromContentScale:(CGFloat)fromContentScale
|
|
980
|
+
fromShadow:(smoothclip::Shadow)fromShadow
|
|
741
981
|
toGeometry:
|
|
742
|
-
(
|
|
982
|
+
(SmoothClipCanonicalGeometry)toGeometry
|
|
743
983
|
hasExplicitToRadii:(BOOL)hasExplicitToRadii
|
|
744
984
|
toContentTranslation:(CGPoint)toContentTranslation
|
|
745
985
|
toContentScale:(CGFloat)toContentScale
|
|
986
|
+
toShadow:(smoothclip::Shadow)toShadow
|
|
746
987
|
duration:(CFTimeInterval)duration
|
|
747
988
|
sharedBeginTime:(CFTimeInterval)sharedBeginTime {
|
|
748
989
|
const int32_t animationId = _activeAnimationId;
|
|
@@ -756,6 +997,9 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
756
997
|
!CGRectIsEmpty(fromGeometry.rect)) {
|
|
757
998
|
[self setClipContainerHidden:NO];
|
|
758
999
|
}
|
|
1000
|
+
// Autonomous native motion has no per-frame accessibility layout updates.
|
|
1001
|
+
// Hide moving descendants until the endpoint is committed.
|
|
1002
|
+
_clipContainer.accessibilityElementsHidden = YES;
|
|
759
1003
|
|
|
760
1004
|
CALayer *layer = _clipContainer.layer;
|
|
761
1005
|
const CGRect toBounds = toGeometry.rect;
|
|
@@ -774,6 +1018,14 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
774
1018
|
[self writeLayerGeometry:toGeometry
|
|
775
1019
|
hasExplicitRadii:hasExplicitToRadii];
|
|
776
1020
|
[self writeContentTranslation:toContentTranslation scale:toContentScale];
|
|
1021
|
+
_canonicalShadow = toShadow;
|
|
1022
|
+
SmoothBoxShadowNormalizeAnimationEndpoints(
|
|
1023
|
+
fromGeometry, &fromShadow, toGeometry, &toShadow);
|
|
1024
|
+
// The target model layer is complete before any presentation animations are
|
|
1025
|
+
// attached. Core Animation may remove the terminal presentation animation
|
|
1026
|
+
// before its delegate runs, so correctness cannot depend on completion-time
|
|
1027
|
+
// cleanup hiding a stale path or opaque color.
|
|
1028
|
+
[self writeShadowModel:toShadow geometry:toGeometry];
|
|
777
1029
|
if (usesMask) {
|
|
778
1030
|
// Keep one mask representation for the full interval, including an
|
|
779
1031
|
// unequal→uniform transition. Switching layer.mask mid-animation would
|
|
@@ -791,10 +1043,41 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
791
1043
|
? SmoothClipCreateRoundedRectPath(
|
|
792
1044
|
toGeometry.rect, toGeometry.radii, toGeometry.curve)
|
|
793
1045
|
: nil;
|
|
794
|
-
|
|
1046
|
+
const BOOL fromShadowVisible =
|
|
1047
|
+
SmoothBoxShadowVisible(fromGeometry, fromShadow);
|
|
1048
|
+
const BOOL toShadowVisible =
|
|
1049
|
+
SmoothBoxShadowVisible(toGeometry, toShadow);
|
|
1050
|
+
const BOOL hasVisibleShadow = fromShadowVisible || toShadowVisible;
|
|
1051
|
+
const BOOL animatesShadowPath = hasVisibleShadow &&
|
|
1052
|
+
!SmoothBoxShadowPathInputEqual(
|
|
1053
|
+
fromGeometry, fromShadow, toGeometry, toShadow);
|
|
1054
|
+
const BOOL animatesShadowColor = hasVisibleShadow &&
|
|
1055
|
+
!SmoothBoxShadowColorEqual(fromShadow, toShadow);
|
|
1056
|
+
const BOOL animatesShadowRadius = hasVisibleShadow &&
|
|
1057
|
+
fromShadow.blurRadius != toShadow.blurRadius;
|
|
1058
|
+
const BOOL animatesShadowOffset = hasVisibleShadow &&
|
|
1059
|
+
(fromShadow.offsetX != toShadow.offsetX ||
|
|
1060
|
+
fromShadow.offsetY != toShadow.offsetY);
|
|
1061
|
+
const BOOL animatesShadow = animatesShadowPath || animatesShadowColor ||
|
|
1062
|
+
animatesShadowRadius || animatesShadowOffset;
|
|
1063
|
+
if (hasVisibleShadow) {
|
|
1064
|
+
[self ensureShadowLayer];
|
|
1065
|
+
// Reveal opacity is encoded in shadowColor alpha. Animating layer opacity
|
|
1066
|
+
// too would multiply both curves and produce a quadratic-looking fade.
|
|
1067
|
+
_shadowLayer.shadowOpacity = 1;
|
|
1068
|
+
}
|
|
1069
|
+
CGPathRef fromShadowPath = animatesShadowPath
|
|
1070
|
+
? SmoothClipCreateShadowPath(fromGeometry, fromShadow)
|
|
1071
|
+
: nil;
|
|
1072
|
+
CGPathRef toShadowPath = animatesShadowPath
|
|
1073
|
+
? SmoothClipCreateShadowPath(toGeometry, toShadow)
|
|
1074
|
+
: nil;
|
|
795
1075
|
CAAnimationGroup *group = [CAAnimationGroup animation];
|
|
796
1076
|
CAAnimationGroup *contentGroup = [CAAnimationGroup animation];
|
|
797
|
-
|
|
1077
|
+
CAAnimationGroup *shadowGroup = animatesShadow
|
|
1078
|
+
? [CAAnimationGroup animation]
|
|
1079
|
+
: nil;
|
|
1080
|
+
CAAnimationGroup *maskAnimation = nil;
|
|
798
1081
|
if (_activeAnimationKind == 1) {
|
|
799
1082
|
CAMediaTimingFunction *timing = [CAMediaTimingFunction
|
|
800
1083
|
functionWithControlPoints:_timingAnimation.controlPoint1X
|
|
@@ -812,10 +1095,21 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
812
1095
|
timingFunction:timing],
|
|
813
1096
|
]];
|
|
814
1097
|
if (usesMask) {
|
|
815
|
-
maskAnimation = [
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1098
|
+
maskAnimation = [CAAnimationGroup animation];
|
|
1099
|
+
maskAnimation.animations = @[
|
|
1100
|
+
[self basicAnimationForKeyPath:@"path"
|
|
1101
|
+
fromValue:(__bridge id)fromMaskPath
|
|
1102
|
+
toValue:(__bridge id)toMaskPath
|
|
1103
|
+
timingFunction:timing],
|
|
1104
|
+
[self basicAnimationForKeyPath:@"bounds"
|
|
1105
|
+
fromValue:[NSValue valueWithCGRect:fromGeometry.rect]
|
|
1106
|
+
toValue:[NSValue valueWithCGRect:toGeometry.rect]
|
|
1107
|
+
timingFunction:timing],
|
|
1108
|
+
[self basicAnimationForKeyPath:@"position"
|
|
1109
|
+
fromValue:[NSValue valueWithCGPoint:fromPosition]
|
|
1110
|
+
toValue:[NSValue valueWithCGPoint:toPosition]
|
|
1111
|
+
timingFunction:timing],
|
|
1112
|
+
];
|
|
819
1113
|
} else {
|
|
820
1114
|
[geometryAnimations addObject:
|
|
821
1115
|
[self basicAnimationForKeyPath:@"cornerRadius"
|
|
@@ -844,6 +1138,33 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
844
1138
|
}
|
|
845
1139
|
contentGroup.animations = contentAnimations;
|
|
846
1140
|
contentGroup.duration = duration;
|
|
1141
|
+
if (animatesShadow) {
|
|
1142
|
+
NSMutableArray<CAAnimation *> *shadowAnimations = [NSMutableArray array];
|
|
1143
|
+
if (animatesShadowPath) [shadowAnimations addObject:
|
|
1144
|
+
[self basicAnimationForKeyPath:@"shadowPath"
|
|
1145
|
+
fromValue:(__bridge id)fromShadowPath
|
|
1146
|
+
toValue:(__bridge id)toShadowPath
|
|
1147
|
+
timingFunction:timing]];
|
|
1148
|
+
if (animatesShadowColor) [shadowAnimations addObject:
|
|
1149
|
+
[self basicAnimationForKeyPath:@"shadowColor"
|
|
1150
|
+
fromValue:(__bridge id)[self colorForShadow:fromShadow]
|
|
1151
|
+
toValue:(__bridge id)[self colorForShadow:toShadow]
|
|
1152
|
+
timingFunction:timing]];
|
|
1153
|
+
if (animatesShadowRadius) [shadowAnimations addObject:
|
|
1154
|
+
[self basicAnimationForKeyPath:@"shadowRadius"
|
|
1155
|
+
fromValue:@(MAX(0, fromShadow.blurRadius) / 2.0)
|
|
1156
|
+
toValue:@(MAX(0, toShadow.blurRadius) / 2.0)
|
|
1157
|
+
timingFunction:timing]];
|
|
1158
|
+
if (animatesShadowOffset) [shadowAnimations addObject:
|
|
1159
|
+
[self basicAnimationForKeyPath:@"shadowOffset"
|
|
1160
|
+
fromValue:[NSValue valueWithCGSize:CGSizeMake(
|
|
1161
|
+
fromShadow.offsetX, fromShadow.offsetY)]
|
|
1162
|
+
toValue:[NSValue valueWithCGSize:CGSizeMake(
|
|
1163
|
+
toShadow.offsetX, toShadow.offsetY)]
|
|
1164
|
+
timingFunction:timing]];
|
|
1165
|
+
shadowGroup.animations = shadowAnimations;
|
|
1166
|
+
shadowGroup.duration = duration;
|
|
1167
|
+
}
|
|
847
1168
|
} else {
|
|
848
1169
|
// `_springAnimation.initialVelocity` is the interactive motion projected
|
|
849
1170
|
// onto the current-to-target trajectory, normalized by that distance
|
|
@@ -888,11 +1209,44 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
888
1209
|
duration:springDuration],
|
|
889
1210
|
]];
|
|
890
1211
|
if (usesMask) {
|
|
891
|
-
maskAnimation = [
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1212
|
+
maskAnimation = [CAAnimationGroup animation];
|
|
1213
|
+
maskAnimation.animations = @[
|
|
1214
|
+
[self springAnimationForKeyPath:@"path"
|
|
1215
|
+
fromValue:(__bridge id)fromMaskPath
|
|
1216
|
+
toValue:(__bridge id)toMaskPath
|
|
1217
|
+
velocity:velocity
|
|
1218
|
+
duration:springDuration],
|
|
1219
|
+
[self springAnimationForKeyPath:@"bounds.origin.x"
|
|
1220
|
+
fromValue:@(fromGeometry.rect.origin.x)
|
|
1221
|
+
toValue:@(toGeometry.rect.origin.x)
|
|
1222
|
+
velocity:velocity
|
|
1223
|
+
duration:springDuration],
|
|
1224
|
+
[self springAnimationForKeyPath:@"bounds.origin.y"
|
|
1225
|
+
fromValue:@(fromGeometry.rect.origin.y)
|
|
1226
|
+
toValue:@(toGeometry.rect.origin.y)
|
|
1227
|
+
velocity:velocity
|
|
1228
|
+
duration:springDuration],
|
|
1229
|
+
[self springAnimationForKeyPath:@"bounds.size.width"
|
|
1230
|
+
fromValue:@(fromGeometry.rect.size.width)
|
|
1231
|
+
toValue:@(toGeometry.rect.size.width)
|
|
1232
|
+
velocity:velocity
|
|
1233
|
+
duration:springDuration],
|
|
1234
|
+
[self springAnimationForKeyPath:@"bounds.size.height"
|
|
1235
|
+
fromValue:@(fromGeometry.rect.size.height)
|
|
1236
|
+
toValue:@(toGeometry.rect.size.height)
|
|
1237
|
+
velocity:velocity
|
|
1238
|
+
duration:springDuration],
|
|
1239
|
+
[self springAnimationForKeyPath:@"position.x"
|
|
1240
|
+
fromValue:@(fromPosition.x)
|
|
1241
|
+
toValue:@(toPosition.x)
|
|
1242
|
+
velocity:velocity
|
|
1243
|
+
duration:springDuration],
|
|
1244
|
+
[self springAnimationForKeyPath:@"position.y"
|
|
1245
|
+
fromValue:@(fromPosition.y)
|
|
1246
|
+
toValue:@(toPosition.y)
|
|
1247
|
+
velocity:velocity
|
|
1248
|
+
duration:springDuration],
|
|
1249
|
+
];
|
|
896
1250
|
} else {
|
|
897
1251
|
[geometryAnimations addObject:
|
|
898
1252
|
[self springAnimationForKeyPath:@"cornerRadius"
|
|
@@ -902,9 +1256,8 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
902
1256
|
duration:springDuration]];
|
|
903
1257
|
}
|
|
904
1258
|
group.animations = geometryAnimations;
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
// time would truncate the oscillation and snap to the target.
|
|
1259
|
+
// Springs use their analytic settling time; a wall-clock duration would
|
|
1260
|
+
// truncate oscillation and snap to the target.
|
|
908
1261
|
group.duration = springDuration;
|
|
909
1262
|
NSMutableArray<CAAnimation *> *contentAnimations = [NSMutableArray arrayWithArray:@[
|
|
910
1263
|
[self springAnimationForKeyPath:@"transform.translation.x"
|
|
@@ -928,6 +1281,37 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
928
1281
|
}
|
|
929
1282
|
contentGroup.animations = contentAnimations;
|
|
930
1283
|
contentGroup.duration = springDuration;
|
|
1284
|
+
if (animatesShadow) {
|
|
1285
|
+
NSMutableArray<CAAnimation *> *shadowAnimations = [NSMutableArray array];
|
|
1286
|
+
if (animatesShadowPath) [shadowAnimations addObject:
|
|
1287
|
+
[self springAnimationForKeyPath:@"shadowPath"
|
|
1288
|
+
fromValue:(__bridge id)fromShadowPath
|
|
1289
|
+
toValue:(__bridge id)toShadowPath
|
|
1290
|
+
velocity:velocity
|
|
1291
|
+
duration:springDuration]];
|
|
1292
|
+
if (animatesShadowColor) [shadowAnimations addObject:
|
|
1293
|
+
[self springAnimationForKeyPath:@"shadowColor"
|
|
1294
|
+
fromValue:(__bridge id)[self colorForShadow:fromShadow]
|
|
1295
|
+
toValue:(__bridge id)[self colorForShadow:toShadow]
|
|
1296
|
+
velocity:velocity
|
|
1297
|
+
duration:springDuration]];
|
|
1298
|
+
if (animatesShadowRadius) [shadowAnimations addObject:
|
|
1299
|
+
[self springAnimationForKeyPath:@"shadowRadius"
|
|
1300
|
+
fromValue:@(MAX(0, fromShadow.blurRadius) / 2.0)
|
|
1301
|
+
toValue:@(MAX(0, toShadow.blurRadius) / 2.0)
|
|
1302
|
+
velocity:velocity
|
|
1303
|
+
duration:springDuration]];
|
|
1304
|
+
if (animatesShadowOffset) [shadowAnimations addObject:
|
|
1305
|
+
[self springAnimationForKeyPath:@"shadowOffset"
|
|
1306
|
+
fromValue:[NSValue valueWithCGSize:CGSizeMake(
|
|
1307
|
+
fromShadow.offsetX, fromShadow.offsetY)]
|
|
1308
|
+
toValue:[NSValue valueWithCGSize:CGSizeMake(
|
|
1309
|
+
toShadow.offsetX, toShadow.offsetY)]
|
|
1310
|
+
velocity:velocity
|
|
1311
|
+
duration:springDuration]];
|
|
1312
|
+
shadowGroup.animations = shadowAnimations;
|
|
1313
|
+
shadowGroup.duration = springDuration;
|
|
1314
|
+
}
|
|
931
1315
|
}
|
|
932
1316
|
|
|
933
1317
|
if (maskAnimation != nil) {
|
|
@@ -942,6 +1326,11 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
942
1326
|
contentGroup.beginTime = [_contentContainer.layer
|
|
943
1327
|
convertTime:sharedBeginTime
|
|
944
1328
|
fromLayer:nil];
|
|
1329
|
+
if (animatesShadow) {
|
|
1330
|
+
shadowGroup.beginTime = [_shadowLayer
|
|
1331
|
+
convertTime:sharedBeginTime
|
|
1332
|
+
fromLayer:nil];
|
|
1333
|
+
}
|
|
945
1334
|
if (maskAnimation != nil) {
|
|
946
1335
|
maskAnimation.beginTime = [_unequalCornerMask
|
|
947
1336
|
convertTime:sharedBeginTime
|
|
@@ -949,10 +1338,6 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
949
1338
|
}
|
|
950
1339
|
}
|
|
951
1340
|
|
|
952
|
-
_animationDuration = group.duration;
|
|
953
|
-
_animationStartedAt = sharedBeginTime > 0
|
|
954
|
-
? sharedBeginTime
|
|
955
|
-
: CACurrentMediaTime();
|
|
956
1341
|
_animationDelegate = [SmoothClipAnimationDelegate new];
|
|
957
1342
|
_animationDelegate.view = self;
|
|
958
1343
|
_animationDelegate.driverId = _driverId;
|
|
@@ -961,27 +1346,30 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
961
1346
|
[layer addAnimation:group forKey:@"smoothClip.geometry"];
|
|
962
1347
|
[_contentContainer.layer addAnimation:contentGroup
|
|
963
1348
|
forKey:@"smoothClip.content"];
|
|
1349
|
+
if (animatesShadow) {
|
|
1350
|
+
[_shadowLayer addAnimation:shadowGroup forKey:@"smoothClip.shadow"];
|
|
1351
|
+
}
|
|
964
1352
|
if (maskAnimation != nil) {
|
|
965
1353
|
[_unequalCornerMask addAnimation:maskAnimation forKey:@"smoothClip.mask"];
|
|
966
1354
|
}
|
|
967
1355
|
if (fromMaskPath != nil) CGPathRelease(fromMaskPath);
|
|
968
1356
|
if (toMaskPath != nil) CGPathRelease(toMaskPath);
|
|
969
|
-
|
|
1357
|
+
if (fromShadowPath != nil) CGPathRelease(fromShadowPath);
|
|
1358
|
+
if (toShadowPath != nil) CGPathRelease(toShadowPath);
|
|
970
1359
|
}
|
|
971
1360
|
|
|
972
1361
|
- (BOOL)startAnimationToRequestedGeometryWithDuration:(CFTimeInterval)duration
|
|
973
1362
|
sharedBeginTime:
|
|
974
1363
|
(CFTimeInterval)sharedBeginTime {
|
|
975
1364
|
if (![self smoothClipCanDisplay]) {
|
|
976
|
-
_pendingAnimationInstall = YES;
|
|
977
1365
|
return NO;
|
|
978
1366
|
}
|
|
979
|
-
|
|
980
|
-
if (![self
|
|
1367
|
+
SmoothClipCanonicalGeometry target;
|
|
1368
|
+
if (![self canonicalRequestedGeometry:&target]) return NO;
|
|
981
1369
|
const smoothclip::Presentation current = [self smoothClipCurrentPresentation];
|
|
982
1370
|
const SmoothClipCornerRadii currentRadii =
|
|
983
1371
|
SmoothClipPresentationRadii(current.clip);
|
|
984
|
-
const
|
|
1372
|
+
const SmoothClipCanonicalGeometry fromGeometry = {
|
|
985
1373
|
.rect = CGRectMake(
|
|
986
1374
|
current.clip.x,
|
|
987
1375
|
current.clip.y,
|
|
@@ -998,10 +1386,12 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
998
1386
|
current.contentTranslateX,
|
|
999
1387
|
current.contentTranslateY)
|
|
1000
1388
|
fromContentScale:current.contentScale
|
|
1389
|
+
fromShadow:current.shadow
|
|
1001
1390
|
toGeometry:target
|
|
1002
1391
|
hasExplicitToRadii:_requestedHasExplicitRadii
|
|
1003
1392
|
toContentTranslation:_requestedContentTranslation
|
|
1004
1393
|
toContentScale:_requestedContentScale
|
|
1394
|
+
toShadow:_requestedShadow
|
|
1005
1395
|
duration:duration
|
|
1006
1396
|
sharedBeginTime:sharedBeginTime];
|
|
1007
1397
|
return YES;
|
|
@@ -1045,12 +1435,13 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1045
1435
|
[self storeRequestedPresentation:presentation];
|
|
1046
1436
|
_springAnimation = animation;
|
|
1047
1437
|
if (animation.inheritVelocity) {
|
|
1048
|
-
|
|
1049
|
-
if ([self
|
|
1438
|
+
SmoothClipCanonicalGeometry target;
|
|
1439
|
+
if ([self canonicalRequestedGeometry:&target]) {
|
|
1050
1440
|
_springAnimation.initialVelocity =
|
|
1051
1441
|
[self inheritedVelocityToRect:target.rect
|
|
1052
1442
|
radii:target.radii
|
|
1053
|
-
contentTranslation:_requestedContentTranslation
|
|
1443
|
+
contentTranslation:_requestedContentTranslation
|
|
1444
|
+
contentScale:_requestedContentScale];
|
|
1054
1445
|
} else {
|
|
1055
1446
|
_springAnimation.initialVelocity = 0;
|
|
1056
1447
|
}
|
|
@@ -1061,201 +1452,6 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1061
1452
|
sharedBeginTime:sharedBeginTime];
|
|
1062
1453
|
}
|
|
1063
1454
|
|
|
1064
|
-
- (CAKeyframeAnimation *)keyframeAnimationForKeyPath:(NSString *)keyPath
|
|
1065
|
-
values:(NSArray *)values
|
|
1066
|
-
keyTimes:(NSArray<NSNumber *> *)keyTimes {
|
|
1067
|
-
CAKeyframeAnimation *animation =
|
|
1068
|
-
[CAKeyframeAnimation animationWithKeyPath:keyPath];
|
|
1069
|
-
animation.values = values;
|
|
1070
|
-
animation.keyTimes = keyTimes;
|
|
1071
|
-
animation.calculationMode = kCAAnimationLinear;
|
|
1072
|
-
return animation;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
- (BOOL)smoothClipAnimateKeyframes:(smoothclip::Presentation)presentation
|
|
1076
|
-
keyframes:(const std::vector<smoothclip::Keyframe> &)keyframes
|
|
1077
|
-
durationMs:(double)durationMs
|
|
1078
|
-
animationId:(int32_t)animationId {
|
|
1079
|
-
return [self smoothClipAnimateKeyframes:presentation
|
|
1080
|
-
keyframes:keyframes
|
|
1081
|
-
durationMs:durationMs
|
|
1082
|
-
animationId:animationId
|
|
1083
|
-
sharedBeginTime:0];
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
- (BOOL)smoothClipAnimateKeyframes:(smoothclip::Presentation)presentation
|
|
1087
|
-
keyframes:(const std::vector<smoothclip::Keyframe> &)keyframes
|
|
1088
|
-
durationMs:(double)durationMs
|
|
1089
|
-
animationId:(int32_t)animationId
|
|
1090
|
-
sharedBeginTime:(CFTimeInterval)sharedBeginTime {
|
|
1091
|
-
if (keyframes.size() < 2) return NO;
|
|
1092
|
-
[self storeRequestedPresentation:presentation];
|
|
1093
|
-
_activeAnimationKind = 3;
|
|
1094
|
-
_activeAnimationId = animationId;
|
|
1095
|
-
if (![self smoothClipCanDisplay]) {
|
|
1096
|
-
_pendingAnimationInstall = YES;
|
|
1097
|
-
return NO;
|
|
1098
|
-
}
|
|
1099
|
-
[self stopLayerAnimationWithoutCallback];
|
|
1100
|
-
_activeAnimationKind = 3;
|
|
1101
|
-
_activeAnimationId = animationId;
|
|
1102
|
-
|
|
1103
|
-
const NSUInteger frameCount = keyframes.size();
|
|
1104
|
-
const CGSize hostSize = self.bounds.size;
|
|
1105
|
-
NSMutableArray<NSNumber *> *times =
|
|
1106
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1107
|
-
NSMutableArray *boundsValues = [NSMutableArray arrayWithCapacity:frameCount];
|
|
1108
|
-
NSMutableArray *positionValues =
|
|
1109
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1110
|
-
NSMutableArray *radiusValues = [NSMutableArray arrayWithCapacity:frameCount];
|
|
1111
|
-
NSMutableArray *maskPathValues =
|
|
1112
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1113
|
-
NSMutableArray *translateXValues =
|
|
1114
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1115
|
-
NSMutableArray *translateYValues =
|
|
1116
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1117
|
-
NSMutableArray *scaleValues =
|
|
1118
|
-
[NSMutableArray arrayWithCapacity:frameCount];
|
|
1119
|
-
BOOL usesMask = NO;
|
|
1120
|
-
BOOL animatesScale = NO;
|
|
1121
|
-
SmoothClipCornerCurve firstCurve = SmoothClipCornerCurveCircular;
|
|
1122
|
-
BOOL hasFirstCurve = NO;
|
|
1123
|
-
std::vector<SmoothNormalizedClipGeometry> normalizedFrames;
|
|
1124
|
-
normalizedFrames.reserve(frameCount);
|
|
1125
|
-
for (const smoothclip::Keyframe &frame : keyframes) {
|
|
1126
|
-
const SmoothClipCornerRadii frameRadii =
|
|
1127
|
-
SmoothClipPresentationRadii(frame.presentation.clip);
|
|
1128
|
-
const SmoothClipCornerCurve frameCurve =
|
|
1129
|
-
SmoothClipPresentationCurve(frame.presentation.clip);
|
|
1130
|
-
SmoothNormalizedClipGeometry normalized;
|
|
1131
|
-
if (!SmoothClipNormalizeGeometryV2(
|
|
1132
|
-
frame.presentation.clip.x,
|
|
1133
|
-
frame.presentation.clip.y,
|
|
1134
|
-
frame.presentation.clip.width,
|
|
1135
|
-
frame.presentation.clip.height,
|
|
1136
|
-
frameRadii.topLeft,
|
|
1137
|
-
frameRadii.topRight,
|
|
1138
|
-
frameRadii.bottomRight,
|
|
1139
|
-
frameRadii.bottomLeft,
|
|
1140
|
-
frameCurve,
|
|
1141
|
-
hostSize,
|
|
1142
|
-
&normalized)) return NO;
|
|
1143
|
-
normalizedFrames.push_back(normalized);
|
|
1144
|
-
if (!SmoothClipCornerRadiiAreUniform(normalized.radii)) usesMask = YES;
|
|
1145
|
-
if (!hasFirstCurve) {
|
|
1146
|
-
firstCurve = normalized.curve;
|
|
1147
|
-
hasFirstCurve = YES;
|
|
1148
|
-
} else if (normalized.curve != firstCurve) {
|
|
1149
|
-
usesMask = YES;
|
|
1150
|
-
}
|
|
1151
|
-
if (frame.presentation.contentScale != 1) animatesScale = YES;
|
|
1152
|
-
[times addObject:@(frame.offset)];
|
|
1153
|
-
[boundsValues addObject:[NSValue valueWithCGRect:normalized.rect]];
|
|
1154
|
-
[positionValues addObject:[NSValue valueWithCGPoint:CGPointMake(
|
|
1155
|
-
CGRectGetMidX(normalized.rect), CGRectGetMidY(normalized.rect))]];
|
|
1156
|
-
[radiusValues addObject:@(normalized.radius)];
|
|
1157
|
-
[translateXValues addObject:@(frame.presentation.contentTranslateX)];
|
|
1158
|
-
[translateYValues addObject:@(frame.presentation.contentTranslateY)];
|
|
1159
|
-
[scaleValues addObject:@(frame.presentation.contentScale)];
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
if (usesMask) {
|
|
1163
|
-
for (const SmoothNormalizedClipGeometry &normalized : normalizedFrames) {
|
|
1164
|
-
CGPathRef path = SmoothClipCreateRoundedRectPath(
|
|
1165
|
-
normalized.rect, normalized.radii, normalized.curve);
|
|
1166
|
-
[maskPathValues addObject:(__bridge id)path];
|
|
1167
|
-
CGPathRelease(path);
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
SmoothNormalizedClipGeometry target;
|
|
1172
|
-
if (![self normalizedRequestedGeometry:&target]) return NO;
|
|
1173
|
-
[self writeLayerGeometry:target
|
|
1174
|
-
hasExplicitRadii:_requestedHasExplicitRadii];
|
|
1175
|
-
[self writeContentTranslation:_requestedContentTranslation
|
|
1176
|
-
scale:_requestedContentScale];
|
|
1177
|
-
if (usesMask) {
|
|
1178
|
-
[self configureUnequalCornerMaskForGeometry:target];
|
|
1179
|
-
_clipContainer.layer.cornerRadius = 0;
|
|
1180
|
-
_clipContainer.layer.mask = _unequalCornerMask;
|
|
1181
|
-
}
|
|
1182
|
-
[self setClipContainerHidden:NO];
|
|
1183
|
-
|
|
1184
|
-
CAAnimationGroup *group = [CAAnimationGroup animation];
|
|
1185
|
-
NSMutableArray<CAAnimation *> *geometryAnimations = [NSMutableArray arrayWithArray:@[
|
|
1186
|
-
[self keyframeAnimationForKeyPath:@"bounds"
|
|
1187
|
-
values:boundsValues
|
|
1188
|
-
keyTimes:times],
|
|
1189
|
-
[self keyframeAnimationForKeyPath:@"position"
|
|
1190
|
-
values:positionValues
|
|
1191
|
-
keyTimes:times],
|
|
1192
|
-
]];
|
|
1193
|
-
if (!usesMask) {
|
|
1194
|
-
[geometryAnimations addObject:
|
|
1195
|
-
[self keyframeAnimationForKeyPath:@"cornerRadius"
|
|
1196
|
-
values:radiusValues
|
|
1197
|
-
keyTimes:times]];
|
|
1198
|
-
}
|
|
1199
|
-
group.animations = geometryAnimations;
|
|
1200
|
-
group.duration = MAX(0, durationMs) / 1000.0;
|
|
1201
|
-
CAAnimationGroup *contentGroup = [CAAnimationGroup animation];
|
|
1202
|
-
NSMutableArray<CAAnimation *> *contentAnimations = [NSMutableArray arrayWithArray:@[
|
|
1203
|
-
[self keyframeAnimationForKeyPath:@"transform.translation.x"
|
|
1204
|
-
values:translateXValues
|
|
1205
|
-
keyTimes:times],
|
|
1206
|
-
[self keyframeAnimationForKeyPath:@"transform.translation.y"
|
|
1207
|
-
values:translateYValues
|
|
1208
|
-
keyTimes:times],
|
|
1209
|
-
]];
|
|
1210
|
-
if (animatesScale) {
|
|
1211
|
-
[contentAnimations addObject:
|
|
1212
|
-
[self keyframeAnimationForKeyPath:@"transform.scale"
|
|
1213
|
-
values:scaleValues
|
|
1214
|
-
keyTimes:times]];
|
|
1215
|
-
}
|
|
1216
|
-
contentGroup.animations = contentAnimations;
|
|
1217
|
-
contentGroup.duration = group.duration;
|
|
1218
|
-
|
|
1219
|
-
CAKeyframeAnimation *maskAnimation = usesMask
|
|
1220
|
-
? [self keyframeAnimationForKeyPath:@"path"
|
|
1221
|
-
values:maskPathValues
|
|
1222
|
-
keyTimes:times]
|
|
1223
|
-
: nil;
|
|
1224
|
-
maskAnimation.duration = group.duration;
|
|
1225
|
-
|
|
1226
|
-
if (sharedBeginTime > 0) {
|
|
1227
|
-
group.beginTime = [_clipContainer.layer
|
|
1228
|
-
convertTime:sharedBeginTime
|
|
1229
|
-
fromLayer:nil];
|
|
1230
|
-
contentGroup.beginTime = [_contentContainer.layer
|
|
1231
|
-
convertTime:sharedBeginTime
|
|
1232
|
-
fromLayer:nil];
|
|
1233
|
-
if (maskAnimation != nil) {
|
|
1234
|
-
maskAnimation.beginTime = [_unequalCornerMask
|
|
1235
|
-
convertTime:sharedBeginTime
|
|
1236
|
-
fromLayer:nil];
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
_animationDuration = group.duration;
|
|
1241
|
-
_animationStartedAt = sharedBeginTime > 0
|
|
1242
|
-
? sharedBeginTime
|
|
1243
|
-
: CACurrentMediaTime();
|
|
1244
|
-
_animationDelegate = [SmoothClipAnimationDelegate new];
|
|
1245
|
-
_animationDelegate.view = self;
|
|
1246
|
-
_animationDelegate.driverId = _driverId;
|
|
1247
|
-
_animationDelegate.animationId = _activeAnimationId;
|
|
1248
|
-
group.delegate = _animationDelegate;
|
|
1249
|
-
[_clipContainer.layer addAnimation:group forKey:@"smoothClip.geometry"];
|
|
1250
|
-
[_contentContainer.layer addAnimation:contentGroup
|
|
1251
|
-
forKey:@"smoothClip.content"];
|
|
1252
|
-
if (maskAnimation != nil) {
|
|
1253
|
-
[_unequalCornerMask addAnimation:maskAnimation forKey:@"smoothClip.mask"];
|
|
1254
|
-
}
|
|
1255
|
-
_animationInstallGeneration += 1;
|
|
1256
|
-
return YES;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
1455
|
- (void)smoothClipCancelAnimationUsingTarget:(BOOL)useTarget {
|
|
1260
1456
|
if (_activeAnimationId == 0) return;
|
|
1261
1457
|
const smoothclip::Presentation visible =
|
|
@@ -1277,8 +1473,11 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1277
1473
|
_activeAnimationKind = 0;
|
|
1278
1474
|
_animationDelegate = nil;
|
|
1279
1475
|
[_unequalCornerMask removeAnimationForKey:@"smoothClip.mask"];
|
|
1280
|
-
[
|
|
1281
|
-
[self
|
|
1476
|
+
[_shadowLayer removeAnimationForKey:@"smoothClip.shadow"];
|
|
1477
|
+
[self applyStaticCornerRepresentation:[self canonicalGeometryValue]];
|
|
1478
|
+
[self writeShadow:_requestedShadow
|
|
1479
|
+
geometry:[self canonicalGeometryValue]];
|
|
1480
|
+
[self syncVisibilityForRect:_canonicalClip];
|
|
1282
1481
|
smoothclip::viewAnimationDidStop(
|
|
1283
1482
|
driverId, animationId, self, finished);
|
|
1284
1483
|
}
|
|
@@ -1288,45 +1487,38 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1288
1487
|
const auto &newProps =
|
|
1289
1488
|
*std::static_pointer_cast<const SmoothClipViewProps>(props);
|
|
1290
1489
|
[super updateProps:props oldProps:oldProps];
|
|
1490
|
+
self.clipsToBounds = YES;
|
|
1291
1491
|
|
|
1292
1492
|
const uint64_t nextDriverId =
|
|
1293
1493
|
isfinite(newProps.driverId) && newProps.driverId > 0
|
|
1294
1494
|
? static_cast<uint64_t>(newProps.driverId)
|
|
1295
1495
|
: 0;
|
|
1296
1496
|
smoothclip::Presentation initial{};
|
|
1297
|
-
if (
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
initial
|
|
1321
|
-
|
|
1322
|
-
newProps.initialClipY,
|
|
1323
|
-
newProps.initialClipWidth,
|
|
1324
|
-
newProps.initialClipHeight,
|
|
1325
|
-
newProps.initialClipRadius},
|
|
1326
|
-
newProps.initialContentTranslateX,
|
|
1327
|
-
newProps.initialContentTranslateY};
|
|
1328
|
-
} else {
|
|
1329
|
-
// A future protocol must not be silently interpreted as V1.
|
|
1497
|
+
if (!SmoothClipBuildPresentation(
|
|
1498
|
+
newProps.initialClipX,
|
|
1499
|
+
newProps.initialClipY,
|
|
1500
|
+
newProps.initialClipWidth,
|
|
1501
|
+
newProps.initialClipHeight,
|
|
1502
|
+
newProps.initialClipTopLeftRadius,
|
|
1503
|
+
newProps.initialClipTopRightRadius,
|
|
1504
|
+
newProps.initialClipBottomRightRadius,
|
|
1505
|
+
newProps.initialClipBottomLeftRadius,
|
|
1506
|
+
newProps.initialClipCurve,
|
|
1507
|
+
newProps.initialContentTranslateX,
|
|
1508
|
+
newProps.initialContentTranslateY,
|
|
1509
|
+
newProps.initialContentScale,
|
|
1510
|
+
newProps.initialClipBoxShadowEnabled,
|
|
1511
|
+
newProps.initialClipBoxShadowRed,
|
|
1512
|
+
newProps.initialClipBoxShadowGreen,
|
|
1513
|
+
newProps.initialClipBoxShadowBlue,
|
|
1514
|
+
newProps.initialClipBoxShadowAlpha,
|
|
1515
|
+
newProps.initialClipBoxShadowOffsetX,
|
|
1516
|
+
newProps.initialClipBoxShadowOffsetY,
|
|
1517
|
+
newProps.initialClipBoxShadowBlurRadius,
|
|
1518
|
+
newProps.initialClipBoxShadowSpreadDistance,
|
|
1519
|
+
&initial)) {
|
|
1520
|
+
// The initial presentation is atomic. Never apply a valid subset when one
|
|
1521
|
+
// field rejects.
|
|
1330
1522
|
return;
|
|
1331
1523
|
}
|
|
1332
1524
|
|
|
@@ -1354,121 +1546,24 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1354
1546
|
|
|
1355
1547
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
1356
1548
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
|
|
1357
|
-
const BOOL wasAnimating = _activeAnimationId != 0;
|
|
1358
|
-
const BOOL wasPending = _pendingAnimationInstall;
|
|
1359
|
-
const uint32_t installGenerationAtEntry = _animationInstallGeneration;
|
|
1360
|
-
const smoothclip::Presentation visible = wasAnimating && !wasPending
|
|
1361
|
-
? [self smoothClipCurrentPresentation]
|
|
1362
|
-
: smoothclip::Presentation{{0, 0, 0, 0, 0}, 0, 0, 1};
|
|
1363
|
-
const CGRect visibleRect = CGRectMake(
|
|
1364
|
-
visible.clip.x,
|
|
1365
|
-
visible.clip.y,
|
|
1366
|
-
visible.clip.width,
|
|
1367
|
-
visible.clip.height);
|
|
1368
|
-
const SmoothClipCornerRadii visibleRadii =
|
|
1369
|
-
SmoothClipPresentationRadii(visible.clip);
|
|
1370
|
-
const SmoothClipCornerCurve visibleCurve =
|
|
1371
|
-
SmoothClipPresentationCurve(visible.clip);
|
|
1372
|
-
const CGPoint visibleContentTranslation = CGPointMake(
|
|
1373
|
-
visible.contentTranslateX,
|
|
1374
|
-
visible.contentTranslateY);
|
|
1375
|
-
const CGFloat visibleContentScale = visible.contentScale;
|
|
1376
|
-
CFTimeInterval remaining = wasAnimating
|
|
1377
|
-
? MAX(0, _animationDuration -
|
|
1378
|
-
(CACurrentMediaTime() - _animationStartedAt))
|
|
1379
|
-
: 0;
|
|
1380
|
-
const double springContinuationVelocity =
|
|
1381
|
-
wasAnimating && !wasPending && _activeAnimationKind == 2
|
|
1382
|
-
? [self smoothClipSpringContinuationVelocity]
|
|
1383
|
-
: 0;
|
|
1384
|
-
|
|
1385
1549
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
1386
1550
|
_hasLayout = YES;
|
|
1387
1551
|
[self layoutContentContainer];
|
|
1388
1552
|
if (_driverId != 0) {
|
|
1389
|
-
// Both edges matter: zero size/detach/background suspend an installed
|
|
1390
|
-
// participant; the first positive visible layout resumes the held latch.
|
|
1391
1553
|
smoothclip::viewDisplayabilityChanged(_driverId, self);
|
|
1392
1554
|
}
|
|
1393
|
-
if (
|
|
1394
|
-
|
|
1395
|
-
// latch. Relayout must not independently restart a background-held id.
|
|
1396
|
-
if (_activeAnimationId == 0) [self applyRequestedClip];
|
|
1397
|
-
return;
|
|
1398
|
-
}
|
|
1399
|
-
if (wasAnimating) {
|
|
1400
|
-
// A negative transition may have frozen this host and re-latched the
|
|
1401
|
-
// registry above. Its non-recording freeze is already the correct model
|
|
1402
|
-
// state for the new lifecycle; do not resurrect a local timer.
|
|
1403
|
-
if (_activeAnimationId == 0 || ![self smoothClipCanDisplay]) return;
|
|
1404
|
-
// The lifecycle notification above may itself have installed this
|
|
1405
|
-
// animation from the registry's trimmed remainder (a latch resume inside
|
|
1406
|
-
// this very layout pass). That install rebased the clocks; the `remaining`
|
|
1407
|
-
// captured on entry predates the suspension and would rebuild a shorter —
|
|
1408
|
-
// possibly zero-duration — run on top of the fresh one. The id alone
|
|
1409
|
-
// cannot detect this (a resume keeps the animation id), so compare the
|
|
1410
|
-
// install generation.
|
|
1411
|
-
if (_animationInstallGeneration != installGenerationAtEntry) return;
|
|
1412
|
-
const int32_t animationId = _activeAnimationId;
|
|
1413
|
-
const NSInteger animationKind = _activeAnimationKind;
|
|
1414
|
-
[self stopLayerAnimationWithoutCallback];
|
|
1415
|
-
_activeAnimationId = animationId;
|
|
1416
|
-
_activeAnimationKind = animationKind;
|
|
1417
|
-
if (_activeAnimationKind == 1 && _animationDuration > 0) {
|
|
1418
|
-
const double progress = smoothclip::clamp01(
|
|
1419
|
-
1 - remaining / _animationDuration);
|
|
1420
|
-
_timingAnimation =
|
|
1421
|
-
smoothclip::timingRemainder(_timingAnimation, progress).animation;
|
|
1422
|
-
remaining = _timingAnimation.durationMs / 1000.0;
|
|
1423
|
-
} else if (_activeAnimationKind == 2) {
|
|
1424
|
-
_springAnimation.initialVelocity = springContinuationVelocity;
|
|
1425
|
-
_springAnimation.inheritVelocity = false;
|
|
1426
|
-
} else if (_activeAnimationKind == 3) {
|
|
1427
|
-
_activeAnimationKind = 1;
|
|
1428
|
-
_timingAnimation = {remaining * 1000.0, 0, 0, 1, 1, 2};
|
|
1429
|
-
}
|
|
1430
|
-
SmoothNormalizedClipGeometry current;
|
|
1431
|
-
SmoothClipNormalizeGeometryV2(
|
|
1432
|
-
CGRectGetMinX(visibleRect),
|
|
1433
|
-
CGRectGetMinY(visibleRect),
|
|
1434
|
-
CGRectGetWidth(visibleRect),
|
|
1435
|
-
CGRectGetHeight(visibleRect),
|
|
1436
|
-
visibleRadii.topLeft,
|
|
1437
|
-
visibleRadii.topRight,
|
|
1438
|
-
visibleRadii.bottomRight,
|
|
1439
|
-
visibleRadii.bottomLeft,
|
|
1440
|
-
visibleCurve,
|
|
1441
|
-
self.bounds.size,
|
|
1442
|
-
¤t);
|
|
1443
|
-
SmoothNormalizedClipGeometry target;
|
|
1444
|
-
if ([self normalizedRequestedGeometry:&target]) {
|
|
1445
|
-
[self installAnimationFromGeometry:current
|
|
1446
|
-
fromContentTranslation:visibleContentTranslation
|
|
1447
|
-
fromContentScale:visibleContentScale
|
|
1448
|
-
toGeometry:target
|
|
1449
|
-
hasExplicitToRadii:_requestedHasExplicitRadii
|
|
1450
|
-
toContentTranslation:_requestedContentTranslation
|
|
1451
|
-
toContentScale:_requestedContentScale
|
|
1452
|
-
duration:remaining
|
|
1453
|
-
sharedBeginTime:0];
|
|
1454
|
-
}
|
|
1455
|
-
} else {
|
|
1456
|
-
if (_driverId != 0 && smoothclip::hasActiveAnimation(_driverId)) {
|
|
1457
|
-
// Registry-level lifecycle pause owns the frozen model. A layout pass
|
|
1458
|
-
// while its latch is held must not apply the requested target or start a
|
|
1459
|
-
// per-view timer; foreground/reattach will normalize and resume it once.
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1555
|
+
if (_activeAnimationId == 0 &&
|
|
1556
|
+
(_driverId == 0 || !smoothclip::hasActiveAnimation(_driverId))) {
|
|
1462
1557
|
[self applyRequestedClip];
|
|
1463
1558
|
}
|
|
1464
1559
|
}
|
|
1465
1560
|
|
|
1466
1561
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
|
|
1467
1562
|
if (![super pointInside:point withEvent:event]) return NO;
|
|
1468
|
-
SmoothClipCornerRadii radii =
|
|
1563
|
+
SmoothClipCornerRadii radii = _canonicalRadii;
|
|
1469
1564
|
const CGRect clip = _activeAnimationId != 0
|
|
1470
1565
|
? [self presentationRectWithRadii:&radii]
|
|
1471
|
-
:
|
|
1566
|
+
: _canonicalClip;
|
|
1472
1567
|
if (CGRectIsEmpty(clip) || !CGRectContainsPoint(clip, point)) return NO;
|
|
1473
1568
|
CGPathRef path = nil;
|
|
1474
1569
|
if (_clipContainer.layer.mask == _unequalCornerMask) {
|
|
@@ -1479,7 +1574,7 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1479
1574
|
path = mask.path;
|
|
1480
1575
|
return path != nil && CGPathContainsPoint(path, NULL, point, NO);
|
|
1481
1576
|
}
|
|
1482
|
-
path = SmoothClipCreateRoundedRectPath(clip, radii,
|
|
1577
|
+
path = SmoothClipCreateRoundedRectPath(clip, radii, _canonicalCurve);
|
|
1483
1578
|
const BOOL contains = CGPathContainsPoint(path, NULL, point, NO);
|
|
1484
1579
|
CGPathRelease(path);
|
|
1485
1580
|
return contains;
|
|
@@ -1491,31 +1586,7 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1491
1586
|
RCTSmoothClipViewHandleCommand(self, commandName, args);
|
|
1492
1587
|
}
|
|
1493
1588
|
|
|
1494
|
-
- (void)setClipGeometry:(double)x
|
|
1495
|
-
y:(double)y
|
|
1496
|
-
width:(double)width
|
|
1497
|
-
height:(double)height
|
|
1498
|
-
radius:(double)radius {
|
|
1499
|
-
_commandIsAuthoritative = YES;
|
|
1500
|
-
[self smoothClipApplyPresentation:
|
|
1501
|
-
{{x, y, width, height, radius}, 0, 0}];
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
1589
|
- (void)setClipPresentation:(double)x
|
|
1505
|
-
y:(double)y
|
|
1506
|
-
width:(double)width
|
|
1507
|
-
height:(double)height
|
|
1508
|
-
radius:(double)radius
|
|
1509
|
-
contentTranslateX:(double)contentTranslateX
|
|
1510
|
-
contentTranslateY:(double)contentTranslateY {
|
|
1511
|
-
_commandIsAuthoritative = YES;
|
|
1512
|
-
[self smoothClipApplyPresentation:
|
|
1513
|
-
{{x, y, width, height, radius},
|
|
1514
|
-
contentTranslateX,
|
|
1515
|
-
contentTranslateY}];
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
- (void)setClipPresentationV2:(double)x
|
|
1519
1590
|
y:(double)y
|
|
1520
1591
|
width:(double)width
|
|
1521
1592
|
height:(double)height
|
|
@@ -1526,9 +1597,18 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1526
1597
|
curveCode:(NSInteger)curveCode
|
|
1527
1598
|
contentTranslateX:(double)contentTranslateX
|
|
1528
1599
|
contentTranslateY:(double)contentTranslateY
|
|
1529
|
-
contentScale:(double)contentScale
|
|
1600
|
+
contentScale:(double)contentScale
|
|
1601
|
+
shadowEnabled:(BOOL)shadowEnabled
|
|
1602
|
+
shadowRed:(double)shadowRed
|
|
1603
|
+
shadowGreen:(double)shadowGreen
|
|
1604
|
+
shadowBlue:(double)shadowBlue
|
|
1605
|
+
shadowAlpha:(double)shadowAlpha
|
|
1606
|
+
shadowOffsetX:(double)shadowOffsetX
|
|
1607
|
+
shadowOffsetY:(double)shadowOffsetY
|
|
1608
|
+
shadowBlurRadius:(double)shadowBlurRadius
|
|
1609
|
+
shadowSpreadDistance:(double)shadowSpreadDistance {
|
|
1530
1610
|
smoothclip::Presentation presentation{};
|
|
1531
|
-
if (!
|
|
1611
|
+
if (!SmoothClipBuildPresentation(
|
|
1532
1612
|
x,
|
|
1533
1613
|
y,
|
|
1534
1614
|
width,
|
|
@@ -1541,6 +1621,15 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1541
1621
|
contentTranslateX,
|
|
1542
1622
|
contentTranslateY,
|
|
1543
1623
|
contentScale,
|
|
1624
|
+
shadowEnabled,
|
|
1625
|
+
shadowRed,
|
|
1626
|
+
shadowGreen,
|
|
1627
|
+
shadowBlue,
|
|
1628
|
+
shadowAlpha,
|
|
1629
|
+
shadowOffsetX,
|
|
1630
|
+
shadowOffsetY,
|
|
1631
|
+
shadowBlurRadius,
|
|
1632
|
+
shadowSpreadDistance,
|
|
1544
1633
|
&presentation)) {
|
|
1545
1634
|
return;
|
|
1546
1635
|
}
|
|
@@ -1554,23 +1643,24 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1554
1643
|
_driverId = 0;
|
|
1555
1644
|
}
|
|
1556
1645
|
[self stopLayerAnimationWithoutCallback];
|
|
1557
|
-
_pendingAnimationInstall = NO;
|
|
1558
1646
|
smoothclip::clearVelocitySamples(_velocitySamples);
|
|
1559
1647
|
[super prepareForRecycle];
|
|
1560
1648
|
_requestedClip = CGRectZero;
|
|
1561
|
-
|
|
1649
|
+
_canonicalClip = CGRectZero;
|
|
1562
1650
|
_requestedRadius = 0;
|
|
1563
|
-
|
|
1651
|
+
_canonicalRadius = 0;
|
|
1564
1652
|
_requestedRadii = {0, 0, 0, 0};
|
|
1565
|
-
|
|
1653
|
+
_canonicalRadii = {0, 0, 0, 0};
|
|
1566
1654
|
_requestedCurve = SmoothClipCornerCurveCircular;
|
|
1567
|
-
|
|
1655
|
+
_canonicalCurve = SmoothClipCornerCurveCircular;
|
|
1568
1656
|
_requestedHasExplicitRadii = NO;
|
|
1569
|
-
|
|
1657
|
+
_canonicalHasExplicitRadii = NO;
|
|
1570
1658
|
_requestedContentTranslation = CGPointZero;
|
|
1571
|
-
|
|
1659
|
+
_canonicalContentTranslation = CGPointZero;
|
|
1572
1660
|
_requestedContentScale = 1;
|
|
1573
|
-
|
|
1661
|
+
_canonicalContentScale = 1;
|
|
1662
|
+
_requestedShadow = {};
|
|
1663
|
+
_canonicalShadow = {};
|
|
1574
1664
|
_hasLayout = NO;
|
|
1575
1665
|
_commandIsAuthoritative = NO;
|
|
1576
1666
|
|
|
@@ -1582,6 +1672,10 @@ static std::array<double, 11> SmoothClipVelocityChannels(
|
|
|
1582
1672
|
layer.mask = nil;
|
|
1583
1673
|
_unequalCornerMask.path = nil;
|
|
1584
1674
|
_contentContainer.layer.affineTransform = CGAffineTransformIdentity;
|
|
1675
|
+
_shadowLayer.shadowPath = nil;
|
|
1676
|
+
_shadowLayer.shadowOpacity = 0;
|
|
1677
|
+
_shadowLayer.shadowRadius = 0;
|
|
1678
|
+
_shadowLayer.shadowOffset = CGSizeZero;
|
|
1585
1679
|
[self setClipContainerHidden:YES];
|
|
1586
1680
|
}
|
|
1587
1681
|
|