react-native-smooth-clip-view 0.2.9 → 0.3.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 +60 -21
- package/android/build.gradle +6 -2
- package/android/proguard-rules.pro +3 -3
- package/android/src/main/cpp/SmoothClipAndroid.h +10 -0
- package/android/src/main/cpp/SmoothClipBindings.cpp +890 -4
- package/android/src/main/cpp/SmoothClipRegistry.cpp +1056 -44
- package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +265 -0
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +21 -0
- package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +283 -0
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +371 -18
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +138 -24
- package/cpp/SmoothClipAnimationCurve.h +151 -122
- package/cpp/SmoothClipRegistry.h +95 -3
- package/cpp/SmoothClipSharedGeometry.h +146 -7
- package/cpp/SmoothClipVelocityTracker.h +13 -9
- package/ios/SmoothClipGeometry.h +184 -6
- package/ios/SmoothClipRegistry.mm +1028 -22
- package/ios/SmoothClipRegistrySnapshot.h +13 -0
- package/ios/SmoothClipTurboModule.cpp +857 -2
- package/ios/SmoothClipTurboModule.h +310 -0
- package/ios/SmoothClipView.mm +714 -183
- package/ios/SmoothClipViewRegistry.h +1 -0
- package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/module/NativeSmoothClipModule.js.map +1 -1
- package/lib/module/SmoothClipView.ios.js +22 -6
- package/lib/module/SmoothClipView.ios.js.map +1 -1
- package/lib/module/SmoothClipView.js +1 -46
- package/lib/module/SmoothClipView.js.map +1 -1
- package/lib/module/SmoothClipViewNativeComponent.ts +27 -1
- package/lib/module/capabilities.js +4 -0
- package/lib/module/capabilities.js.map +1 -0
- package/lib/module/capabilities.native.js +36 -0
- package/lib/module/capabilities.native.js.map +1 -0
- package/lib/module/capabilityTypes.js +11 -0
- package/lib/module/capabilityTypes.js.map +1 -0
- package/lib/module/driverState.js +0 -28
- package/lib/module/driverState.js.map +1 -1
- package/lib/module/drivers.js +1 -281
- package/lib/module/drivers.js.map +1 -1
- package/lib/module/drivers.native.js +263 -63
- package/lib/module/drivers.native.js.map +1 -1
- package/lib/module/geometry.js +147 -17
- package/lib/module/geometry.js.map +1 -1
- package/lib/module/groupDriverTypes.js +4 -0
- package/lib/module/groupDriverTypes.js.map +1 -0
- package/lib/module/groupDrivers.android.js +4 -0
- package/lib/module/groupDrivers.android.js.map +1 -0
- package/lib/module/groupDrivers.ios.js +4 -0
- package/lib/module/groupDrivers.ios.js.map +1 -0
- package/lib/module/groupDrivers.js +4 -0
- package/lib/module/groupDrivers.js.map +1 -0
- package/lib/module/groupDrivers.native.js +799 -0
- package/lib/module/groupDrivers.native.js.map +1 -0
- package/lib/module/index.js +3 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/presentationProtocol.js +15 -0
- package/lib/module/presentationProtocol.js.map +1 -0
- package/lib/typescript/src/NativeSmoothClipModule.d.ts +25 -0
- package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts +2 -10
- package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.ios.d.ts +133 -2
- package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +8 -0
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/capabilities.d.ts +3 -0
- package/lib/typescript/src/capabilities.d.ts.map +1 -0
- package/lib/typescript/src/capabilities.native.d.ts +3 -0
- package/lib/typescript/src/capabilities.native.d.ts.map +1 -0
- package/lib/typescript/src/capabilityTypes.d.ts +10 -0
- package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
- package/lib/typescript/src/driverState.d.ts +1 -6
- package/lib/typescript/src/driverState.d.ts.map +1 -1
- package/lib/typescript/src/driverTypes.d.ts +25 -11
- package/lib/typescript/src/driverTypes.d.ts.map +1 -1
- package/lib/typescript/src/drivers.d.ts +2 -4
- package/lib/typescript/src/drivers.d.ts.map +1 -1
- package/lib/typescript/src/drivers.native.d.ts.map +1 -1
- package/lib/typescript/src/geometry.d.ts +36 -2
- package/lib/typescript/src/geometry.d.ts.map +1 -1
- package/lib/typescript/src/groupDriverTypes.d.ts +66 -0
- package/lib/typescript/src/groupDriverTypes.d.ts.map +1 -0
- package/lib/typescript/src/groupDrivers.android.d.ts +2 -0
- package/lib/typescript/src/groupDrivers.android.d.ts.map +1 -0
- package/lib/typescript/src/groupDrivers.d.ts +2 -0
- package/lib/typescript/src/groupDrivers.d.ts.map +1 -0
- package/lib/typescript/src/groupDrivers.ios.d.ts +2 -0
- package/lib/typescript/src/groupDrivers.ios.d.ts.map +1 -0
- package/lib/typescript/src/groupDrivers.native.d.ts +4 -0
- package/lib/typescript/src/groupDrivers.native.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/presentationProtocol.d.ts +4 -0
- package/lib/typescript/src/presentationProtocol.d.ts.map +1 -0
- package/package.json +10 -11
- package/src/NativeSmoothClipModule.ts +275 -0
- package/src/SmoothClipView.ios.tsx +30 -11
- package/src/SmoothClipView.tsx +2 -76
- package/src/SmoothClipViewNativeComponent.ts +27 -1
- package/src/capabilities.native.ts +69 -0
- package/src/capabilities.ts +2 -0
- package/src/capabilityTypes.ts +17 -0
- package/src/driverState.ts +1 -41
- package/src/driverTypes.ts +42 -11
- package/src/drivers.native.ts +824 -124
- package/src/drivers.ts +2 -423
- package/src/geometry.ts +231 -27
- package/src/groupDriverTypes.ts +117 -0
- package/src/groupDrivers.android.ts +1 -0
- package/src/groupDrivers.ios.ts +1 -0
- package/src/groupDrivers.native.ts +1189 -0
- package/src/groupDrivers.ts +1 -0
- package/src/index.ts +30 -2
- package/src/presentationProtocol.ts +28 -0
- package/lib/module/SmoothClipView.web.js +0 -54
- package/lib/module/SmoothClipView.web.js.map +0 -1
- package/lib/module/index.web.js +0 -8
- package/lib/module/index.web.js.map +0 -1
- package/lib/module/webClipPath.js +0 -25
- package/lib/module/webClipPath.js.map +0 -1
- package/lib/typescript/src/SmoothClipView.web.d.ts +0 -10
- package/lib/typescript/src/SmoothClipView.web.d.ts.map +0 -1
- package/lib/typescript/src/index.web.d.ts +0 -9
- package/lib/typescript/src/index.web.d.ts.map +0 -1
- package/lib/typescript/src/webClipPath.d.ts +0 -7
- package/lib/typescript/src/webClipPath.d.ts.map +0 -1
- package/src/SmoothClipView.web.tsx +0 -66
- package/src/index.web.ts +0 -23
- package/src/webClipPath.ts +0 -30
package/ios/SmoothClipView.mm
CHANGED
|
@@ -51,26 +51,164 @@ using namespace facebook::react;
|
|
|
51
51
|
finished:(BOOL)finished;
|
|
52
52
|
@end
|
|
53
53
|
|
|
54
|
-
static
|
|
55
|
-
|
|
54
|
+
static SmoothClipCornerRadii SmoothClipPresentationRadii(
|
|
55
|
+
const smoothclip::Geometry &geometry) {
|
|
56
|
+
return {
|
|
57
|
+
(CGFloat)smoothclip::resolvedRadius(
|
|
58
|
+
geometry.topLeftRadius, geometry.radius),
|
|
59
|
+
(CGFloat)smoothclip::resolvedRadius(
|
|
60
|
+
geometry.topRightRadius, geometry.radius),
|
|
61
|
+
(CGFloat)smoothclip::resolvedRadius(
|
|
62
|
+
geometry.bottomRightRadius, geometry.radius),
|
|
63
|
+
(CGFloat)smoothclip::resolvedRadius(
|
|
64
|
+
geometry.bottomLeftRadius, geometry.radius),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static BOOL SmoothClipPresentationHasExplicitRadii(
|
|
69
|
+
const smoothclip::Geometry &geometry) {
|
|
70
|
+
return isfinite(geometry.topLeftRadius) ||
|
|
71
|
+
isfinite(geometry.topRightRadius) ||
|
|
72
|
+
isfinite(geometry.bottomRightRadius) ||
|
|
73
|
+
isfinite(geometry.bottomLeftRadius);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static SmoothClipCornerCurve SmoothClipPresentationCurve(
|
|
77
|
+
const smoothclip::Geometry &geometry) {
|
|
78
|
+
return geometry.curve == smoothclip::ClipCurve::Continuous
|
|
79
|
+
? SmoothClipCornerCurveContinuous
|
|
80
|
+
: SmoothClipCornerCurveCircular;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static bool SmoothClipBuildPresentationV2(
|
|
84
|
+
double x,
|
|
85
|
+
double y,
|
|
86
|
+
double width,
|
|
87
|
+
double height,
|
|
88
|
+
double topLeftRadius,
|
|
89
|
+
double topRightRadius,
|
|
90
|
+
double bottomRightRadius,
|
|
91
|
+
double bottomLeftRadius,
|
|
92
|
+
NSInteger curveCode,
|
|
93
|
+
double contentTranslateX,
|
|
94
|
+
double contentTranslateY,
|
|
95
|
+
double contentScale,
|
|
96
|
+
smoothclip::Presentation *result) {
|
|
97
|
+
if (result == nullptr || !isfinite(x) || !isfinite(y) ||
|
|
98
|
+
!isfinite(width) || !isfinite(height) ||
|
|
99
|
+
!isfinite(topLeftRadius) || !isfinite(topRightRadius) ||
|
|
100
|
+
!isfinite(bottomRightRadius) || !isfinite(bottomLeftRadius) ||
|
|
101
|
+
!isfinite(contentTranslateX) || !isfinite(contentTranslateY) ||
|
|
102
|
+
!isfinite(contentScale) || contentScale <= 0 ||
|
|
103
|
+
(curveCode != static_cast<NSInteger>(smoothclip::ClipCurve::Circular) &&
|
|
104
|
+
curveCode != static_cast<NSInteger>(smoothclip::ClipCurve::Continuous))) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const bool uniform = topLeftRadius == topRightRadius &&
|
|
109
|
+
topLeftRadius == bottomRightRadius &&
|
|
110
|
+
topLeftRadius == bottomLeftRadius;
|
|
111
|
+
smoothclip::Geometry geometry{
|
|
112
|
+
x, y, width, height, uniform ? topLeftRadius : 0};
|
|
113
|
+
geometry.topLeftRadius = topLeftRadius;
|
|
114
|
+
geometry.topRightRadius = topRightRadius;
|
|
115
|
+
geometry.bottomRightRadius = bottomRightRadius;
|
|
116
|
+
geometry.bottomLeftRadius = bottomLeftRadius;
|
|
117
|
+
geometry.curve = static_cast<smoothclip::ClipCurve>(curveCode);
|
|
118
|
+
*result = {
|
|
119
|
+
geometry, contentTranslateX, contentTranslateY, contentScale};
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static NSString *SmoothClipCALayerCornerCurve(
|
|
124
|
+
SmoothClipCornerCurve curve) API_AVAILABLE(ios(13.0)) {
|
|
125
|
+
return curve == SmoothClipCornerCurveContinuous
|
|
126
|
+
? kCACornerCurveContinuous
|
|
127
|
+
: kCACornerCurveCircular;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static CGAffineTransform SmoothClipContentTransform(
|
|
131
|
+
CGFloat scale,
|
|
132
|
+
CGPoint translation) {
|
|
133
|
+
// Construct the matrix directly. Concatenating a translation and a scale is
|
|
134
|
+
// order-sensitive and can multiply tx/ty; the presentation contract requires
|
|
135
|
+
// a centered uniform scale with translation remaining in unscaled points.
|
|
136
|
+
return CGAffineTransformMake(
|
|
137
|
+
scale, 0, 0, scale, translation.x, translation.y);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
struct SmoothClipPathEndpoints {
|
|
141
|
+
CGPoint points[9];
|
|
142
|
+
size_t count;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
static void SmoothClipCollectPathEndpoints(
|
|
146
|
+
void *context,
|
|
147
|
+
const CGPathElement *element) {
|
|
148
|
+
SmoothClipPathEndpoints *endpoints =
|
|
149
|
+
static_cast<SmoothClipPathEndpoints *>(context);
|
|
150
|
+
if (endpoints->count >= 9 ||
|
|
151
|
+
element->type == kCGPathElementCloseSubpath) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const size_t endpointIndex =
|
|
155
|
+
element->type == kCGPathElementAddCurveToPoint ? 2 :
|
|
156
|
+
element->type == kCGPathElementAddQuadCurveToPoint ? 1 : 0;
|
|
157
|
+
endpoints->points[endpoints->count++] = element->points[endpointIndex];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static SmoothClipCornerRadii SmoothClipRadiiFromFixedPath(
|
|
161
|
+
CGPathRef path,
|
|
162
|
+
CGRect rect,
|
|
163
|
+
SmoothClipCornerRadii fallback) {
|
|
164
|
+
if (path == nil) return fallback;
|
|
165
|
+
SmoothClipPathEndpoints endpoints{};
|
|
166
|
+
CGPathApply(path, &endpoints, SmoothClipCollectPathEndpoints);
|
|
167
|
+
if (endpoints.count != 9) return fallback;
|
|
168
|
+
return {
|
|
169
|
+
MAX(0, endpoints.points[0].x - CGRectGetMinX(rect)),
|
|
170
|
+
MAX(0, CGRectGetMaxX(rect) - endpoints.points[1].x),
|
|
171
|
+
MAX(0, CGRectGetMaxY(rect) - endpoints.points[3].y),
|
|
172
|
+
MAX(0, endpoints.points[5].x - CGRectGetMinX(rect)),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static std::array<double, 11> SmoothClipVelocityChannels(
|
|
177
|
+
CGRect rect,
|
|
178
|
+
SmoothClipCornerRadii radii,
|
|
179
|
+
CGPoint contentTranslation,
|
|
180
|
+
CGFloat contentScale) {
|
|
56
181
|
return {CGRectGetMinX(rect),
|
|
57
182
|
CGRectGetMinY(rect),
|
|
58
183
|
CGRectGetWidth(rect),
|
|
59
184
|
CGRectGetHeight(rect),
|
|
60
|
-
|
|
185
|
+
radii.topLeft,
|
|
186
|
+
radii.topRight,
|
|
187
|
+
radii.bottomRight,
|
|
188
|
+
radii.bottomLeft,
|
|
61
189
|
contentTranslation.x,
|
|
62
|
-
contentTranslation.y
|
|
190
|
+
contentTranslation.y,
|
|
191
|
+
contentScale};
|
|
63
192
|
}
|
|
64
193
|
|
|
65
194
|
@implementation SmoothClipView {
|
|
66
195
|
SmoothClipContainerView *_clipContainer;
|
|
67
196
|
SmoothClipContainerView *_contentContainer;
|
|
197
|
+
CAShapeLayer *_unequalCornerMask;
|
|
68
198
|
CGRect _requestedClip;
|
|
69
199
|
CGRect _normalizedClip;
|
|
70
200
|
CGFloat _requestedRadius;
|
|
71
201
|
CGFloat _normalizedRadius;
|
|
202
|
+
SmoothClipCornerRadii _requestedRadii;
|
|
203
|
+
SmoothClipCornerRadii _normalizedRadii;
|
|
204
|
+
SmoothClipCornerCurve _requestedCurve;
|
|
205
|
+
SmoothClipCornerCurve _normalizedCurve;
|
|
206
|
+
BOOL _requestedHasExplicitRadii;
|
|
207
|
+
BOOL _normalizedHasExplicitRadii;
|
|
72
208
|
CGPoint _requestedContentTranslation;
|
|
73
209
|
CGPoint _normalizedContentTranslation;
|
|
210
|
+
CGFloat _requestedContentScale;
|
|
211
|
+
CGFloat _normalizedContentScale;
|
|
74
212
|
uint64_t _driverId;
|
|
75
213
|
BOOL _hasLayout;
|
|
76
214
|
BOOL _commandIsAuthoritative;
|
|
@@ -139,12 +277,29 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
139
277
|
[_clipContainer addSubview:_contentContainer];
|
|
140
278
|
[self addSubview:_clipContainer];
|
|
141
279
|
|
|
280
|
+
_unequalCornerMask = [CAShapeLayer layer];
|
|
281
|
+
_unequalCornerMask.actions = @{
|
|
282
|
+
@"path": [NSNull null],
|
|
283
|
+
@"bounds": [NSNull null],
|
|
284
|
+
@"position": [NSNull null],
|
|
285
|
+
};
|
|
286
|
+
_unequalCornerMask.fillColor = UIColor.blackColor.CGColor;
|
|
287
|
+
_unequalCornerMask.needsDisplayOnBoundsChange = NO;
|
|
288
|
+
|
|
142
289
|
_requestedClip = CGRectZero;
|
|
143
290
|
_normalizedClip = CGRectZero;
|
|
144
291
|
_requestedRadius = 0;
|
|
145
292
|
_normalizedRadius = 0;
|
|
293
|
+
_requestedRadii = {0, 0, 0, 0};
|
|
294
|
+
_normalizedRadii = {0, 0, 0, 0};
|
|
295
|
+
_requestedCurve = SmoothClipCornerCurveCircular;
|
|
296
|
+
_normalizedCurve = SmoothClipCornerCurveCircular;
|
|
297
|
+
_requestedHasExplicitRadii = NO;
|
|
298
|
+
_normalizedHasExplicitRadii = NO;
|
|
146
299
|
_requestedContentTranslation = CGPointZero;
|
|
147
300
|
_normalizedContentTranslation = CGPointZero;
|
|
301
|
+
_requestedContentScale = 1;
|
|
302
|
+
_normalizedContentScale = 1;
|
|
148
303
|
_driverId = 0;
|
|
149
304
|
_hasLayout = NO;
|
|
150
305
|
_commandIsAuthoritative = NO;
|
|
@@ -198,10 +353,17 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
198
353
|
geometry.y,
|
|
199
354
|
MAX(0, geometry.width),
|
|
200
355
|
MAX(0, geometry.height));
|
|
201
|
-
|
|
356
|
+
_requestedRadii = SmoothClipPresentationRadii(geometry);
|
|
357
|
+
_requestedRadius = SmoothClipCornerRadiiAreUniform(_requestedRadii)
|
|
358
|
+
? MAX(0, _requestedRadii.topLeft)
|
|
359
|
+
: 0;
|
|
360
|
+
_requestedCurve = SmoothClipPresentationCurve(geometry);
|
|
361
|
+
_requestedHasExplicitRadii =
|
|
362
|
+
SmoothClipPresentationHasExplicitRadii(geometry);
|
|
202
363
|
_requestedContentTranslation = CGPointMake(
|
|
203
364
|
presentation.contentTranslateX,
|
|
204
365
|
presentation.contentTranslateY);
|
|
366
|
+
_requestedContentScale = presentation.contentScale;
|
|
205
367
|
}
|
|
206
368
|
|
|
207
369
|
- (BOOL)normalizedRequestedGeometry:(SmoothNormalizedClipGeometry *)geometry {
|
|
@@ -217,12 +379,16 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
217
379
|
}
|
|
218
380
|
#endif
|
|
219
381
|
const CGSize hostSize = self.bounds.size;
|
|
220
|
-
const BOOL valid =
|
|
382
|
+
const BOOL valid = SmoothClipNormalizeGeometryV2(
|
|
221
383
|
CGRectGetMinX(_requestedClip),
|
|
222
384
|
CGRectGetMinY(_requestedClip),
|
|
223
385
|
CGRectGetWidth(_requestedClip),
|
|
224
386
|
CGRectGetHeight(_requestedClip),
|
|
225
|
-
|
|
387
|
+
_requestedRadii.topLeft,
|
|
388
|
+
_requestedRadii.topRight,
|
|
389
|
+
_requestedRadii.bottomRight,
|
|
390
|
+
_requestedRadii.bottomLeft,
|
|
391
|
+
_requestedCurve,
|
|
226
392
|
CGSizeMake(MAX(0, hostSize.width), MAX(0, hostSize.height)),
|
|
227
393
|
geometry);
|
|
228
394
|
#if defined(SMOOTH_CLIP_ENABLE_SIGNPOSTS) && SMOOTH_CLIP_ENABLE_SIGNPOSTS
|
|
@@ -245,12 +411,61 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
245
411
|
[self setClipContainerHidden:CGRectIsEmpty(rect)];
|
|
246
412
|
}
|
|
247
413
|
|
|
248
|
-
- (void)
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
414
|
+
- (void)configureUnequalCornerMaskForGeometry:
|
|
415
|
+
(SmoothNormalizedClipGeometry)geometry {
|
|
416
|
+
const CGSize hostSize = self.bounds.size;
|
|
417
|
+
_unequalCornerMask.bounds = CGRectMake(
|
|
418
|
+
0, 0, MAX(0, hostSize.width), MAX(0, hostSize.height));
|
|
419
|
+
_unequalCornerMask.position = CGPointMake(
|
|
420
|
+
MAX(0, hostSize.width) / 2, MAX(0, hostSize.height) / 2);
|
|
421
|
+
CGPathRef path = SmoothClipCreateRoundedRectPath(
|
|
422
|
+
geometry.rect, geometry.radii, geometry.curve);
|
|
423
|
+
_unequalCornerMask.path = path;
|
|
424
|
+
CGPathRelease(path);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
- (void)applyStaticCornerRepresentation:
|
|
428
|
+
(SmoothNormalizedClipGeometry)geometry {
|
|
429
|
+
CALayer *layer = _clipContainer.layer;
|
|
430
|
+
if (SmoothClipCornerRadiiAreUniform(geometry.radii)) {
|
|
431
|
+
layer.mask = nil;
|
|
432
|
+
layer.cornerRadius = geometry.radii.topLeft;
|
|
433
|
+
layer.cornerCurve = SmoothClipCALayerCornerCurve(geometry.curve);
|
|
434
|
+
} else {
|
|
435
|
+
layer.cornerRadius = 0;
|
|
436
|
+
layer.cornerCurve = kCACornerCurveCircular;
|
|
437
|
+
[self configureUnequalCornerMaskForGeometry:geometry];
|
|
438
|
+
layer.mask = _unequalCornerMask;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
- (SmoothNormalizedClipGeometry)normalizedGeometryValue {
|
|
443
|
+
return {
|
|
444
|
+
.rect = _normalizedClip,
|
|
445
|
+
.radius = _normalizedRadius,
|
|
446
|
+
.radii = _normalizedRadii,
|
|
447
|
+
.curve = _normalizedCurve,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
- (void)writeLayerGeometry:(SmoothNormalizedClipGeometry)geometry
|
|
452
|
+
hasExplicitRadii:(BOOL)hasExplicitRadii {
|
|
453
|
+
const BOOL rectChanged =
|
|
454
|
+
!CGRectEqualToRect(_normalizedClip, geometry.rect);
|
|
455
|
+
const BOOL radiiChanged =
|
|
456
|
+
!SmoothClipCornerRadiiEqual(_normalizedRadii, geometry.radii);
|
|
457
|
+
const BOOL curveChanged = _normalizedCurve != geometry.curve;
|
|
458
|
+
const BOOL representationChanged =
|
|
459
|
+
_normalizedHasExplicitRadii != hasExplicitRadii;
|
|
460
|
+
_normalizedClip = geometry.rect;
|
|
461
|
+
_normalizedRadius = geometry.radius;
|
|
462
|
+
_normalizedRadii = geometry.radii;
|
|
463
|
+
_normalizedCurve = geometry.curve;
|
|
464
|
+
_normalizedHasExplicitRadii = hasExplicitRadii;
|
|
465
|
+
if (!rectChanged && !radiiChanged && !curveChanged &&
|
|
466
|
+
!representationChanged) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
254
469
|
|
|
255
470
|
#if defined(SMOOTH_CLIP_ENABLE_SIGNPOSTS) && SMOOTH_CLIP_ENABLE_SIGNPOSTS
|
|
256
471
|
const bool signpostsEnabled =
|
|
@@ -264,12 +479,13 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
264
479
|
#endif
|
|
265
480
|
CALayer *layer = _clipContainer.layer;
|
|
266
481
|
if (rectChanged) {
|
|
267
|
-
layer.bounds = rect;
|
|
268
|
-
layer.position = CGPointMake(
|
|
269
|
-
|
|
270
|
-
if (radiusChanged) {
|
|
271
|
-
layer.cornerRadius = radius;
|
|
482
|
+
layer.bounds = geometry.rect;
|
|
483
|
+
layer.position = CGPointMake(
|
|
484
|
+
CGRectGetMidX(geometry.rect), CGRectGetMidY(geometry.rect));
|
|
272
485
|
}
|
|
486
|
+
// The uniform common case stays on CALayer's compositor fast path and uses
|
|
487
|
+
// the platform's exact continuous-corner implementation.
|
|
488
|
+
[self applyStaticCornerRepresentation:geometry];
|
|
273
489
|
#if defined(SMOOTH_CLIP_ENABLE_SIGNPOSTS) && SMOOTH_CLIP_ENABLE_SIGNPOSTS
|
|
274
490
|
if (signpostsEnabled) {
|
|
275
491
|
os_signpost_interval_end(
|
|
@@ -278,11 +494,16 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
278
494
|
#endif
|
|
279
495
|
}
|
|
280
496
|
|
|
281
|
-
- (void)writeContentTranslation:(CGPoint)translation
|
|
282
|
-
|
|
497
|
+
- (void)writeContentTranslation:(CGPoint)translation
|
|
498
|
+
scale:(CGFloat)scale {
|
|
499
|
+
if (CGPointEqualToPoint(_normalizedContentTranslation, translation) &&
|
|
500
|
+
_normalizedContentScale == scale) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
283
503
|
_normalizedContentTranslation = translation;
|
|
504
|
+
_normalizedContentScale = scale;
|
|
284
505
|
_contentContainer.layer.affineTransform =
|
|
285
|
-
|
|
506
|
+
SmoothClipContentTransform(scale, translation);
|
|
286
507
|
}
|
|
287
508
|
|
|
288
509
|
- (void)applyRequestedClip {
|
|
@@ -291,37 +512,47 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
291
512
|
|
|
292
513
|
const BOOL visibilityChanged = _clipHidden != CGRectIsEmpty(geometry.rect);
|
|
293
514
|
if (CGRectEqualToRect(_normalizedClip, geometry.rect) &&
|
|
294
|
-
|
|
515
|
+
SmoothClipCornerRadiiEqual(_normalizedRadii, geometry.radii) &&
|
|
516
|
+
_normalizedCurve == geometry.curve &&
|
|
517
|
+
_normalizedHasExplicitRadii == _requestedHasExplicitRadii &&
|
|
295
518
|
CGPointEqualToPoint(
|
|
296
519
|
_normalizedContentTranslation, _requestedContentTranslation) &&
|
|
520
|
+
_normalizedContentScale == _requestedContentScale &&
|
|
297
521
|
!visibilityChanged) {
|
|
298
522
|
return;
|
|
299
523
|
}
|
|
300
|
-
[self
|
|
301
|
-
|
|
524
|
+
[self writeLayerGeometry:geometry
|
|
525
|
+
hasExplicitRadii:_requestedHasExplicitRadii];
|
|
526
|
+
[self writeContentTranslation:_requestedContentTranslation
|
|
527
|
+
scale:_requestedContentScale];
|
|
302
528
|
[self syncVisibilityForRect:geometry.rect];
|
|
303
529
|
}
|
|
304
530
|
|
|
305
531
|
- (void)recordInteractiveRect:(CGRect)rect
|
|
306
|
-
|
|
307
|
-
contentTranslation:(CGPoint)contentTranslation
|
|
532
|
+
radii:(SmoothClipCornerRadii)radii
|
|
533
|
+
contentTranslation:(CGPoint)contentTranslation
|
|
534
|
+
contentScale:(CGFloat)contentScale {
|
|
308
535
|
// Identical-value dedupe and same-frame coalescing live in the shared
|
|
309
536
|
// tracker; coalescing keeps a distinct re-record issued sub-frame after
|
|
310
537
|
// the last one (e.g. a fused animateTo `from` seed at gesture release)
|
|
311
538
|
// from exploding the inherited velocity.
|
|
312
539
|
smoothclip::recordVelocitySample(
|
|
313
540
|
_velocitySamples,
|
|
314
|
-
SmoothClipVelocityChannels(
|
|
541
|
+
SmoothClipVelocityChannels(
|
|
542
|
+
rect, radii, contentTranslation, contentScale),
|
|
315
543
|
CACurrentMediaTime());
|
|
316
544
|
}
|
|
317
545
|
|
|
318
546
|
- (CGFloat)inheritedVelocityToRect:(CGRect)target
|
|
319
|
-
|
|
547
|
+
radii:(SmoothClipCornerRadii)targetRadii
|
|
320
548
|
contentTranslation:(CGPoint)targetContentTranslation {
|
|
321
549
|
return smoothclip::inheritedVelocity(
|
|
322
550
|
_velocitySamples,
|
|
323
551
|
SmoothClipVelocityChannels(
|
|
324
|
-
target,
|
|
552
|
+
target,
|
|
553
|
+
targetRadii,
|
|
554
|
+
targetContentTranslation,
|
|
555
|
+
_requestedContentScale),
|
|
325
556
|
CACurrentMediaTime());
|
|
326
557
|
}
|
|
327
558
|
|
|
@@ -338,29 +569,40 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
338
569
|
// cancel-to-target): they must not enter the 'inherit' history.
|
|
339
570
|
if (recordVelocitySample && _hasLayout) {
|
|
340
571
|
[self recordInteractiveRect:_normalizedClip
|
|
341
|
-
|
|
342
|
-
contentTranslation:_normalizedContentTranslation
|
|
572
|
+
radii:_normalizedRadii
|
|
573
|
+
contentTranslation:_normalizedContentTranslation
|
|
574
|
+
contentScale:_normalizedContentScale];
|
|
343
575
|
}
|
|
344
576
|
}
|
|
345
577
|
|
|
346
578
|
- (smoothclip::Presentation)smoothClipCurrentPresentation {
|
|
347
|
-
|
|
579
|
+
SmoothClipCornerRadii visibleRadii = _normalizedRadii;
|
|
348
580
|
const CGRect visibleRect = _activeAnimationId != 0
|
|
349
|
-
? [self
|
|
581
|
+
? [self presentationRectWithRadii:&visibleRadii]
|
|
350
582
|
: _normalizedClip;
|
|
351
583
|
CALayer *contentLayer = _activeAnimationId != 0
|
|
352
584
|
? (CALayer *)_contentContainer.layer.presentationLayer
|
|
353
585
|
: _contentContainer.layer;
|
|
354
586
|
if (contentLayer == nil) contentLayer = _contentContainer.layer;
|
|
355
587
|
const CGAffineTransform transform = contentLayer.affineTransform;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
588
|
+
const BOOL uniform = SmoothClipCornerRadiiAreUniform(visibleRadii);
|
|
589
|
+
smoothclip::Geometry geometry{
|
|
590
|
+
CGRectGetMinX(visibleRect),
|
|
591
|
+
CGRectGetMinY(visibleRect),
|
|
592
|
+
CGRectGetWidth(visibleRect),
|
|
593
|
+
CGRectGetHeight(visibleRect),
|
|
594
|
+
uniform ? visibleRadii.topLeft : 0};
|
|
595
|
+
if (_normalizedHasExplicitRadii || !uniform) {
|
|
596
|
+
geometry.topLeftRadius = visibleRadii.topLeft;
|
|
597
|
+
geometry.topRightRadius = visibleRadii.topRight;
|
|
598
|
+
geometry.bottomRightRadius = visibleRadii.bottomRight;
|
|
599
|
+
geometry.bottomLeftRadius = visibleRadii.bottomLeft;
|
|
600
|
+
}
|
|
601
|
+
geometry.curve = _normalizedCurve == SmoothClipCornerCurveContinuous
|
|
602
|
+
? smoothclip::ClipCurve::Continuous
|
|
603
|
+
: smoothclip::ClipCurve::Circular;
|
|
604
|
+
const CGFloat contentScale = hypot(transform.a, transform.b);
|
|
605
|
+
return {geometry, transform.tx, transform.ty, contentScale};
|
|
364
606
|
}
|
|
365
607
|
|
|
366
608
|
- (BOOL)smoothClipIsJoinable {
|
|
@@ -398,35 +640,39 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
398
640
|
- (smoothclip::Presentation)smoothClipFreezePresentation {
|
|
399
641
|
const smoothclip::Presentation visible =
|
|
400
642
|
[self smoothClipCurrentPresentation];
|
|
401
|
-
const CGRect visibleRect = CGRectMake(
|
|
402
|
-
visible.clip.x,
|
|
403
|
-
visible.clip.y,
|
|
404
|
-
visible.clip.width,
|
|
405
|
-
visible.clip.height);
|
|
406
|
-
const CGPoint visibleContentTranslation = CGPointMake(
|
|
407
|
-
visible.contentTranslateX,
|
|
408
|
-
visible.contentTranslateY);
|
|
409
643
|
[self stopLayerAnimationWithoutCallback];
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
_requestedContentTranslation = visibleContentTranslation;
|
|
413
|
-
[self writeLayerRect:visibleRect radius:visible.clip.radius];
|
|
414
|
-
[self writeContentTranslation:visibleContentTranslation];
|
|
415
|
-
[self syncVisibilityForRect:visibleRect];
|
|
644
|
+
[self storeRequestedPresentation:visible];
|
|
645
|
+
[self applyRequestedClip];
|
|
416
646
|
return visible;
|
|
417
647
|
}
|
|
418
648
|
|
|
419
|
-
- (CGRect)
|
|
649
|
+
- (CGRect)presentationRectWithRadii:(SmoothClipCornerRadii *)radii {
|
|
420
650
|
CALayer *layer = (CALayer *)_clipContainer.layer.presentationLayer;
|
|
421
651
|
if (layer == nil) layer = _clipContainer.layer;
|
|
422
652
|
const CGRect bounds = layer.bounds;
|
|
423
653
|
const CGPoint position = layer.position;
|
|
424
|
-
|
|
425
|
-
return CGRectMake(
|
|
654
|
+
const CGRect rect = CGRectMake(
|
|
426
655
|
position.x - bounds.size.width * layer.anchorPoint.x,
|
|
427
656
|
position.y - bounds.size.height * layer.anchorPoint.y,
|
|
428
657
|
bounds.size.width,
|
|
429
658
|
bounds.size.height);
|
|
659
|
+
if (radii != nullptr) {
|
|
660
|
+
if (_clipContainer.layer.mask == _unequalCornerMask) {
|
|
661
|
+
CAShapeLayer *mask =
|
|
662
|
+
(CAShapeLayer *)_unequalCornerMask.presentationLayer;
|
|
663
|
+
if (mask == nil) mask = _unequalCornerMask;
|
|
664
|
+
*radii = SmoothClipRadiiFromFixedPath(
|
|
665
|
+
mask.path, rect, _normalizedRadii);
|
|
666
|
+
} else {
|
|
667
|
+
*radii = {
|
|
668
|
+
layer.cornerRadius,
|
|
669
|
+
layer.cornerRadius,
|
|
670
|
+
layer.cornerRadius,
|
|
671
|
+
layer.cornerRadius,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return rect;
|
|
430
676
|
}
|
|
431
677
|
|
|
432
678
|
- (void)stopLayerAnimationWithoutCallback {
|
|
@@ -440,6 +686,8 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
440
686
|
_ignoreAnimationCallback = YES;
|
|
441
687
|
[_clipContainer.layer removeAnimationForKey:@"smoothClip.geometry"];
|
|
442
688
|
[_contentContainer.layer removeAnimationForKey:@"smoothClip.content"];
|
|
689
|
+
[_unequalCornerMask removeAnimationForKey:@"smoothClip.mask"];
|
|
690
|
+
[self applyStaticCornerRepresentation:[self normalizedGeometryValue]];
|
|
443
691
|
_ignoreAnimationCallback = NO;
|
|
444
692
|
_activeAnimationId = 0;
|
|
445
693
|
_activeAnimationKind = 0;
|
|
@@ -486,13 +734,17 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
486
734
|
return animation;
|
|
487
735
|
}
|
|
488
736
|
|
|
489
|
-
- (void)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
737
|
+
- (void)installAnimationFromGeometry:
|
|
738
|
+
(SmoothNormalizedClipGeometry)fromGeometry
|
|
739
|
+
fromContentTranslation:(CGPoint)fromContentTranslation
|
|
740
|
+
fromContentScale:(CGFloat)fromContentScale
|
|
741
|
+
toGeometry:
|
|
742
|
+
(SmoothNormalizedClipGeometry)toGeometry
|
|
743
|
+
hasExplicitToRadii:(BOOL)hasExplicitToRadii
|
|
744
|
+
toContentTranslation:(CGPoint)toContentTranslation
|
|
745
|
+
toContentScale:(CGFloat)toContentScale
|
|
746
|
+
duration:(CFTimeInterval)duration
|
|
747
|
+
sharedBeginTime:(CFTimeInterval)sharedBeginTime {
|
|
496
748
|
const int32_t animationId = _activeAnimationId;
|
|
497
749
|
const NSInteger animationKind = _activeAnimationKind;
|
|
498
750
|
[self stopLayerAnimationWithoutCallback];
|
|
@@ -500,50 +752,80 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
500
752
|
_activeAnimationKind = animationKind;
|
|
501
753
|
// Unhide while any animated frame can show content; an empty-to-empty
|
|
502
754
|
// transition must stay hidden for accessibility.
|
|
503
|
-
if (!CGRectIsEmpty(
|
|
755
|
+
if (!CGRectIsEmpty(toGeometry.rect) ||
|
|
756
|
+
!CGRectIsEmpty(fromGeometry.rect)) {
|
|
504
757
|
[self setClipContainerHidden:NO];
|
|
505
758
|
}
|
|
506
759
|
|
|
507
760
|
CALayer *layer = _clipContainer.layer;
|
|
508
|
-
const CGRect toBounds =
|
|
761
|
+
const CGRect toBounds = toGeometry.rect;
|
|
509
762
|
const CGPoint fromPosition =
|
|
510
|
-
CGPointMake(
|
|
763
|
+
CGPointMake(
|
|
764
|
+
CGRectGetMidX(fromGeometry.rect),
|
|
765
|
+
CGRectGetMidY(fromGeometry.rect));
|
|
511
766
|
const CGPoint toPosition =
|
|
512
|
-
CGPointMake(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
767
|
+
CGPointMake(
|
|
768
|
+
CGRectGetMidX(toGeometry.rect),
|
|
769
|
+
CGRectGetMidY(toGeometry.rect));
|
|
770
|
+
const BOOL usesMask =
|
|
771
|
+
!SmoothClipCornerRadiiAreUniform(fromGeometry.radii) ||
|
|
772
|
+
!SmoothClipCornerRadiiAreUniform(toGeometry.radii) ||
|
|
773
|
+
fromGeometry.curve != toGeometry.curve;
|
|
774
|
+
[self writeLayerGeometry:toGeometry
|
|
775
|
+
hasExplicitRadii:hasExplicitToRadii];
|
|
776
|
+
[self writeContentTranslation:toContentTranslation scale:toContentScale];
|
|
777
|
+
if (usesMask) {
|
|
778
|
+
// Keep one mask representation for the full interval, including an
|
|
779
|
+
// unequal→uniform transition. Switching layer.mask mid-animation would
|
|
780
|
+
// create a one-frame seam even though the underlying paths are compatible.
|
|
781
|
+
[self configureUnequalCornerMaskForGeometry:toGeometry];
|
|
782
|
+
layer.cornerRadius = 0;
|
|
783
|
+
layer.mask = _unequalCornerMask;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
CGPathRef fromMaskPath = usesMask
|
|
787
|
+
? SmoothClipCreateRoundedRectPath(
|
|
788
|
+
fromGeometry.rect, fromGeometry.radii, fromGeometry.curve)
|
|
789
|
+
: nil;
|
|
790
|
+
CGPathRef toMaskPath = usesMask
|
|
791
|
+
? SmoothClipCreateRoundedRectPath(
|
|
792
|
+
toGeometry.rect, toGeometry.radii, toGeometry.curve)
|
|
793
|
+
: nil;
|
|
522
794
|
|
|
523
795
|
CAAnimationGroup *group = [CAAnimationGroup animation];
|
|
524
796
|
CAAnimationGroup *contentGroup = [CAAnimationGroup animation];
|
|
797
|
+
CAAnimation *maskAnimation = nil;
|
|
525
798
|
if (_activeAnimationKind == 1) {
|
|
526
799
|
CAMediaTimingFunction *timing = [CAMediaTimingFunction
|
|
527
800
|
functionWithControlPoints:_timingAnimation.controlPoint1X
|
|
528
801
|
:_timingAnimation.controlPoint1Y
|
|
529
802
|
:_timingAnimation.controlPoint2X
|
|
530
803
|
:_timingAnimation.controlPoint2Y];
|
|
531
|
-
|
|
804
|
+
NSMutableArray<CAAnimation *> *geometryAnimations = [NSMutableArray arrayWithArray:@[
|
|
532
805
|
[self basicAnimationForKeyPath:@"bounds"
|
|
533
|
-
fromValue:[NSValue valueWithCGRect:
|
|
806
|
+
fromValue:[NSValue valueWithCGRect:fromGeometry.rect]
|
|
534
807
|
toValue:[NSValue valueWithCGRect:toBounds]
|
|
535
808
|
timingFunction:timing],
|
|
536
809
|
[self basicAnimationForKeyPath:@"position"
|
|
537
810
|
fromValue:[NSValue valueWithCGPoint:fromPosition]
|
|
538
811
|
toValue:[NSValue valueWithCGPoint:toPosition]
|
|
539
812
|
timingFunction:timing],
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
813
|
+
]];
|
|
814
|
+
if (usesMask) {
|
|
815
|
+
maskAnimation = [self basicAnimationForKeyPath:@"path"
|
|
816
|
+
fromValue:(__bridge id)fromMaskPath
|
|
817
|
+
toValue:(__bridge id)toMaskPath
|
|
818
|
+
timingFunction:timing];
|
|
819
|
+
} else {
|
|
820
|
+
[geometryAnimations addObject:
|
|
821
|
+
[self basicAnimationForKeyPath:@"cornerRadius"
|
|
822
|
+
fromValue:@(fromGeometry.radii.topLeft)
|
|
823
|
+
toValue:@(toGeometry.radii.topLeft)
|
|
824
|
+
timingFunction:timing]];
|
|
825
|
+
}
|
|
826
|
+
group.animations = geometryAnimations;
|
|
545
827
|
group.duration = duration;
|
|
546
|
-
|
|
828
|
+
NSMutableArray<CAAnimation *> *contentAnimations = [NSMutableArray arrayWithArray:@[
|
|
547
829
|
[self basicAnimationForKeyPath:@"transform.translation.x"
|
|
548
830
|
fromValue:@(fromContentTranslation.x)
|
|
549
831
|
toValue:@(toContentTranslation.x)
|
|
@@ -552,7 +834,15 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
552
834
|
fromValue:@(fromContentTranslation.y)
|
|
553
835
|
toValue:@(toContentTranslation.y)
|
|
554
836
|
timingFunction:timing],
|
|
555
|
-
];
|
|
837
|
+
]];
|
|
838
|
+
if (fromContentScale != 1 || toContentScale != 1) {
|
|
839
|
+
[contentAnimations addObject:
|
|
840
|
+
[self basicAnimationForKeyPath:@"transform.scale"
|
|
841
|
+
fromValue:@(fromContentScale)
|
|
842
|
+
toValue:@(toContentScale)
|
|
843
|
+
timingFunction:timing]];
|
|
844
|
+
}
|
|
845
|
+
contentGroup.animations = contentAnimations;
|
|
556
846
|
contentGroup.duration = duration;
|
|
557
847
|
} else {
|
|
558
848
|
// `_springAnimation.initialVelocity` is the interactive motion projected
|
|
@@ -565,24 +855,24 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
565
855
|
const double velocity = _springAnimation.initialVelocity;
|
|
566
856
|
const CFTimeInterval springDuration =
|
|
567
857
|
[self springSettlingDurationWithVelocity:velocity];
|
|
568
|
-
|
|
858
|
+
NSMutableArray<CAAnimation *> *geometryAnimations = [NSMutableArray arrayWithArray:@[
|
|
569
859
|
[self springAnimationForKeyPath:@"bounds.origin.x"
|
|
570
|
-
fromValue:@(
|
|
860
|
+
fromValue:@(fromGeometry.rect.origin.x)
|
|
571
861
|
toValue:@(toBounds.origin.x)
|
|
572
862
|
velocity:velocity
|
|
573
863
|
duration:springDuration],
|
|
574
864
|
[self springAnimationForKeyPath:@"bounds.origin.y"
|
|
575
|
-
fromValue:@(
|
|
865
|
+
fromValue:@(fromGeometry.rect.origin.y)
|
|
576
866
|
toValue:@(toBounds.origin.y)
|
|
577
867
|
velocity:velocity
|
|
578
868
|
duration:springDuration],
|
|
579
869
|
[self springAnimationForKeyPath:@"bounds.size.width"
|
|
580
|
-
fromValue:@(
|
|
870
|
+
fromValue:@(fromGeometry.rect.size.width)
|
|
581
871
|
toValue:@(toBounds.size.width)
|
|
582
872
|
velocity:velocity
|
|
583
873
|
duration:springDuration],
|
|
584
874
|
[self springAnimationForKeyPath:@"bounds.size.height"
|
|
585
|
-
fromValue:@(
|
|
875
|
+
fromValue:@(fromGeometry.rect.size.height)
|
|
586
876
|
toValue:@(toBounds.size.height)
|
|
587
877
|
velocity:velocity
|
|
588
878
|
duration:springDuration],
|
|
@@ -596,17 +886,27 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
596
886
|
toValue:@(toPosition.y)
|
|
597
887
|
velocity:velocity
|
|
598
888
|
duration:springDuration],
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
889
|
+
]];
|
|
890
|
+
if (usesMask) {
|
|
891
|
+
maskAnimation = [self springAnimationForKeyPath:@"path"
|
|
892
|
+
fromValue:(__bridge id)fromMaskPath
|
|
893
|
+
toValue:(__bridge id)toMaskPath
|
|
894
|
+
velocity:velocity
|
|
895
|
+
duration:springDuration];
|
|
896
|
+
} else {
|
|
897
|
+
[geometryAnimations addObject:
|
|
898
|
+
[self springAnimationForKeyPath:@"cornerRadius"
|
|
899
|
+
fromValue:@(fromGeometry.radii.topLeft)
|
|
900
|
+
toValue:@(toGeometry.radii.topLeft)
|
|
901
|
+
velocity:velocity
|
|
902
|
+
duration:springDuration]];
|
|
903
|
+
}
|
|
904
|
+
group.animations = geometryAnimations;
|
|
605
905
|
// A rebuilt spring (layout change, foreground resume) must run its own
|
|
606
906
|
// settling time; clamping to the interrupted run's remaining wall-clock
|
|
607
907
|
// time would truncate the oscillation and snap to the target.
|
|
608
908
|
group.duration = springDuration;
|
|
609
|
-
|
|
909
|
+
NSMutableArray<CAAnimation *> *contentAnimations = [NSMutableArray arrayWithArray:@[
|
|
610
910
|
[self springAnimationForKeyPath:@"transform.translation.x"
|
|
611
911
|
fromValue:@(fromContentTranslation.x)
|
|
612
912
|
toValue:@(toContentTranslation.x)
|
|
@@ -617,12 +917,42 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
617
917
|
toValue:@(toContentTranslation.y)
|
|
618
918
|
velocity:velocity
|
|
619
919
|
duration:springDuration],
|
|
620
|
-
];
|
|
920
|
+
]];
|
|
921
|
+
if (fromContentScale != 1 || toContentScale != 1) {
|
|
922
|
+
[contentAnimations addObject:
|
|
923
|
+
[self springAnimationForKeyPath:@"transform.scale"
|
|
924
|
+
fromValue:@(fromContentScale)
|
|
925
|
+
toValue:@(toContentScale)
|
|
926
|
+
velocity:velocity
|
|
927
|
+
duration:springDuration]];
|
|
928
|
+
}
|
|
929
|
+
contentGroup.animations = contentAnimations;
|
|
621
930
|
contentGroup.duration = springDuration;
|
|
622
931
|
}
|
|
623
932
|
|
|
933
|
+
if (maskAnimation != nil) {
|
|
934
|
+
maskAnimation.duration = group.duration;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (sharedBeginTime > 0) {
|
|
938
|
+
// Each layer receives the same absolute media timestamp translated into
|
|
939
|
+
// its local clock. Group participants therefore advance from one epoch
|
|
940
|
+
// even though their animations are installed sequentially.
|
|
941
|
+
group.beginTime = [layer convertTime:sharedBeginTime fromLayer:nil];
|
|
942
|
+
contentGroup.beginTime = [_contentContainer.layer
|
|
943
|
+
convertTime:sharedBeginTime
|
|
944
|
+
fromLayer:nil];
|
|
945
|
+
if (maskAnimation != nil) {
|
|
946
|
+
maskAnimation.beginTime = [_unequalCornerMask
|
|
947
|
+
convertTime:sharedBeginTime
|
|
948
|
+
fromLayer:nil];
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
624
952
|
_animationDuration = group.duration;
|
|
625
|
-
_animationStartedAt =
|
|
953
|
+
_animationStartedAt = sharedBeginTime > 0
|
|
954
|
+
? sharedBeginTime
|
|
955
|
+
: CACurrentMediaTime();
|
|
626
956
|
_animationDelegate = [SmoothClipAnimationDelegate new];
|
|
627
957
|
_animationDelegate.view = self;
|
|
628
958
|
_animationDelegate.driverId = _driverId;
|
|
@@ -631,10 +961,17 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
631
961
|
[layer addAnimation:group forKey:@"smoothClip.geometry"];
|
|
632
962
|
[_contentContainer.layer addAnimation:contentGroup
|
|
633
963
|
forKey:@"smoothClip.content"];
|
|
964
|
+
if (maskAnimation != nil) {
|
|
965
|
+
[_unequalCornerMask addAnimation:maskAnimation forKey:@"smoothClip.mask"];
|
|
966
|
+
}
|
|
967
|
+
if (fromMaskPath != nil) CGPathRelease(fromMaskPath);
|
|
968
|
+
if (toMaskPath != nil) CGPathRelease(toMaskPath);
|
|
634
969
|
_animationInstallGeneration += 1;
|
|
635
970
|
}
|
|
636
971
|
|
|
637
|
-
- (BOOL)startAnimationToRequestedGeometryWithDuration:(CFTimeInterval)duration
|
|
972
|
+
- (BOOL)startAnimationToRequestedGeometryWithDuration:(CFTimeInterval)duration
|
|
973
|
+
sharedBeginTime:
|
|
974
|
+
(CFTimeInterval)sharedBeginTime {
|
|
638
975
|
if (![self smoothClipCanDisplay]) {
|
|
639
976
|
_pendingAnimationInstall = YES;
|
|
640
977
|
return NO;
|
|
@@ -642,36 +979,69 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
642
979
|
SmoothNormalizedClipGeometry target;
|
|
643
980
|
if (![self normalizedRequestedGeometry:&target]) return NO;
|
|
644
981
|
const smoothclip::Presentation current = [self smoothClipCurrentPresentation];
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
982
|
+
const SmoothClipCornerRadii currentRadii =
|
|
983
|
+
SmoothClipPresentationRadii(current.clip);
|
|
984
|
+
const SmoothNormalizedClipGeometry fromGeometry = {
|
|
985
|
+
.rect = CGRectMake(
|
|
986
|
+
current.clip.x,
|
|
987
|
+
current.clip.y,
|
|
988
|
+
current.clip.width,
|
|
989
|
+
current.clip.height),
|
|
990
|
+
.radius = SmoothClipCornerRadiiAreUniform(currentRadii)
|
|
991
|
+
? currentRadii.topLeft
|
|
992
|
+
: 0,
|
|
993
|
+
.radii = currentRadii,
|
|
994
|
+
.curve = SmoothClipPresentationCurve(current.clip),
|
|
995
|
+
};
|
|
996
|
+
[self installAnimationFromGeometry:fromGeometry
|
|
997
|
+
fromContentTranslation:CGPointMake(
|
|
998
|
+
current.contentTranslateX,
|
|
999
|
+
current.contentTranslateY)
|
|
1000
|
+
fromContentScale:current.contentScale
|
|
1001
|
+
toGeometry:target
|
|
1002
|
+
hasExplicitToRadii:_requestedHasExplicitRadii
|
|
1003
|
+
toContentTranslation:_requestedContentTranslation
|
|
1004
|
+
toContentScale:_requestedContentScale
|
|
1005
|
+
duration:duration
|
|
1006
|
+
sharedBeginTime:sharedBeginTime];
|
|
658
1007
|
return YES;
|
|
659
1008
|
}
|
|
660
1009
|
|
|
661
1010
|
- (BOOL)smoothClipAnimateTiming:(smoothclip::Presentation)presentation
|
|
662
1011
|
animation:(smoothclip::TimingAnimation)animation
|
|
663
1012
|
animationId:(int32_t)animationId {
|
|
1013
|
+
return [self smoothClipAnimateTiming:presentation
|
|
1014
|
+
animation:animation
|
|
1015
|
+
animationId:animationId
|
|
1016
|
+
sharedBeginTime:0];
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
- (BOOL)smoothClipAnimateTiming:(smoothclip::Presentation)presentation
|
|
1020
|
+
animation:(smoothclip::TimingAnimation)animation
|
|
1021
|
+
animationId:(int32_t)animationId
|
|
1022
|
+
sharedBeginTime:(CFTimeInterval)sharedBeginTime {
|
|
664
1023
|
[self storeRequestedPresentation:presentation];
|
|
665
1024
|
_timingAnimation = animation;
|
|
666
1025
|
_activeAnimationKind = 1;
|
|
667
1026
|
_activeAnimationId = animationId;
|
|
668
1027
|
return [self startAnimationToRequestedGeometryWithDuration:
|
|
669
|
-
MAX(0, animation.durationMs) / 1000.0
|
|
1028
|
+
MAX(0, animation.durationMs) / 1000.0
|
|
1029
|
+
sharedBeginTime:sharedBeginTime];
|
|
670
1030
|
}
|
|
671
1031
|
|
|
672
1032
|
- (BOOL)smoothClipAnimateSpring:(smoothclip::Presentation)presentation
|
|
673
1033
|
animation:(smoothclip::SpringAnimation)animation
|
|
674
1034
|
animationId:(int32_t)animationId {
|
|
1035
|
+
return [self smoothClipAnimateSpring:presentation
|
|
1036
|
+
animation:animation
|
|
1037
|
+
animationId:animationId
|
|
1038
|
+
sharedBeginTime:0];
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
- (BOOL)smoothClipAnimateSpring:(smoothclip::Presentation)presentation
|
|
1042
|
+
animation:(smoothclip::SpringAnimation)animation
|
|
1043
|
+
animationId:(int32_t)animationId
|
|
1044
|
+
sharedBeginTime:(CFTimeInterval)sharedBeginTime {
|
|
675
1045
|
[self storeRequestedPresentation:presentation];
|
|
676
1046
|
_springAnimation = animation;
|
|
677
1047
|
if (animation.inheritVelocity) {
|
|
@@ -679,7 +1049,7 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
679
1049
|
if ([self normalizedRequestedGeometry:&target]) {
|
|
680
1050
|
_springAnimation.initialVelocity =
|
|
681
1051
|
[self inheritedVelocityToRect:target.rect
|
|
682
|
-
|
|
1052
|
+
radii:target.radii
|
|
683
1053
|
contentTranslation:_requestedContentTranslation];
|
|
684
1054
|
} else {
|
|
685
1055
|
_springAnimation.initialVelocity = 0;
|
|
@@ -687,7 +1057,8 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
687
1057
|
}
|
|
688
1058
|
_activeAnimationKind = 2;
|
|
689
1059
|
_activeAnimationId = animationId;
|
|
690
|
-
return [self startAnimationToRequestedGeometryWithDuration:0
|
|
1060
|
+
return [self startAnimationToRequestedGeometryWithDuration:0
|
|
1061
|
+
sharedBeginTime:sharedBeginTime];
|
|
691
1062
|
}
|
|
692
1063
|
|
|
693
1064
|
- (CAKeyframeAnimation *)keyframeAnimationForKeyPath:(NSString *)keyPath
|
|
@@ -705,6 +1076,18 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
705
1076
|
keyframes:(const std::vector<smoothclip::Keyframe> &)keyframes
|
|
706
1077
|
durationMs:(double)durationMs
|
|
707
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 {
|
|
708
1091
|
if (keyframes.size() < 2) return NO;
|
|
709
1092
|
[self storeRequestedPresentation:presentation];
|
|
710
1093
|
_activeAnimationKind = 3;
|
|
@@ -725,20 +1108,47 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
725
1108
|
NSMutableArray *positionValues =
|
|
726
1109
|
[NSMutableArray arrayWithCapacity:frameCount];
|
|
727
1110
|
NSMutableArray *radiusValues = [NSMutableArray arrayWithCapacity:frameCount];
|
|
1111
|
+
NSMutableArray *maskPathValues =
|
|
1112
|
+
[NSMutableArray arrayWithCapacity:frameCount];
|
|
728
1113
|
NSMutableArray *translateXValues =
|
|
729
1114
|
[NSMutableArray arrayWithCapacity:frameCount];
|
|
730
1115
|
NSMutableArray *translateYValues =
|
|
731
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);
|
|
732
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);
|
|
733
1130
|
SmoothNormalizedClipGeometry normalized;
|
|
734
|
-
if (!
|
|
1131
|
+
if (!SmoothClipNormalizeGeometryV2(
|
|
735
1132
|
frame.presentation.clip.x,
|
|
736
1133
|
frame.presentation.clip.y,
|
|
737
1134
|
frame.presentation.clip.width,
|
|
738
1135
|
frame.presentation.clip.height,
|
|
739
|
-
|
|
1136
|
+
frameRadii.topLeft,
|
|
1137
|
+
frameRadii.topRight,
|
|
1138
|
+
frameRadii.bottomRight,
|
|
1139
|
+
frameRadii.bottomLeft,
|
|
1140
|
+
frameCurve,
|
|
740
1141
|
hostSize,
|
|
741
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;
|
|
742
1152
|
[times addObject:@(frame.offset)];
|
|
743
1153
|
[boundsValues addObject:[NSValue valueWithCGRect:normalized.rect]];
|
|
744
1154
|
[positionValues addObject:[NSValue valueWithCGPoint:CGPointMake(
|
|
@@ -746,40 +1156,91 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
746
1156
|
[radiusValues addObject:@(normalized.radius)];
|
|
747
1157
|
[translateXValues addObject:@(frame.presentation.contentTranslateX)];
|
|
748
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
|
+
}
|
|
749
1169
|
}
|
|
750
1170
|
|
|
751
1171
|
SmoothNormalizedClipGeometry target;
|
|
752
1172
|
if (![self normalizedRequestedGeometry:&target]) return NO;
|
|
753
|
-
[self
|
|
754
|
-
|
|
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
|
+
}
|
|
755
1182
|
[self setClipContainerHidden:NO];
|
|
756
1183
|
|
|
757
1184
|
CAAnimationGroup *group = [CAAnimationGroup animation];
|
|
758
|
-
|
|
1185
|
+
NSMutableArray<CAAnimation *> *geometryAnimations = [NSMutableArray arrayWithArray:@[
|
|
759
1186
|
[self keyframeAnimationForKeyPath:@"bounds"
|
|
760
1187
|
values:boundsValues
|
|
761
1188
|
keyTimes:times],
|
|
762
1189
|
[self keyframeAnimationForKeyPath:@"position"
|
|
763
1190
|
values:positionValues
|
|
764
1191
|
keyTimes:times],
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1192
|
+
]];
|
|
1193
|
+
if (!usesMask) {
|
|
1194
|
+
[geometryAnimations addObject:
|
|
1195
|
+
[self keyframeAnimationForKeyPath:@"cornerRadius"
|
|
1196
|
+
values:radiusValues
|
|
1197
|
+
keyTimes:times]];
|
|
1198
|
+
}
|
|
1199
|
+
group.animations = geometryAnimations;
|
|
769
1200
|
group.duration = MAX(0, durationMs) / 1000.0;
|
|
770
1201
|
CAAnimationGroup *contentGroup = [CAAnimationGroup animation];
|
|
771
|
-
|
|
1202
|
+
NSMutableArray<CAAnimation *> *contentAnimations = [NSMutableArray arrayWithArray:@[
|
|
772
1203
|
[self keyframeAnimationForKeyPath:@"transform.translation.x"
|
|
773
1204
|
values:translateXValues
|
|
774
1205
|
keyTimes:times],
|
|
775
1206
|
[self keyframeAnimationForKeyPath:@"transform.translation.y"
|
|
776
1207
|
values:translateYValues
|
|
777
1208
|
keyTimes:times],
|
|
778
|
-
];
|
|
1209
|
+
]];
|
|
1210
|
+
if (animatesScale) {
|
|
1211
|
+
[contentAnimations addObject:
|
|
1212
|
+
[self keyframeAnimationForKeyPath:@"transform.scale"
|
|
1213
|
+
values:scaleValues
|
|
1214
|
+
keyTimes:times]];
|
|
1215
|
+
}
|
|
1216
|
+
contentGroup.animations = contentAnimations;
|
|
779
1217
|
contentGroup.duration = group.duration;
|
|
780
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
|
+
|
|
781
1240
|
_animationDuration = group.duration;
|
|
782
|
-
_animationStartedAt =
|
|
1241
|
+
_animationStartedAt = sharedBeginTime > 0
|
|
1242
|
+
? sharedBeginTime
|
|
1243
|
+
: CACurrentMediaTime();
|
|
783
1244
|
_animationDelegate = [SmoothClipAnimationDelegate new];
|
|
784
1245
|
_animationDelegate.view = self;
|
|
785
1246
|
_animationDelegate.driverId = _driverId;
|
|
@@ -788,6 +1249,9 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
788
1249
|
[_clipContainer.layer addAnimation:group forKey:@"smoothClip.geometry"];
|
|
789
1250
|
[_contentContainer.layer addAnimation:contentGroup
|
|
790
1251
|
forKey:@"smoothClip.content"];
|
|
1252
|
+
if (maskAnimation != nil) {
|
|
1253
|
+
[_unequalCornerMask addAnimation:maskAnimation forKey:@"smoothClip.mask"];
|
|
1254
|
+
}
|
|
791
1255
|
_animationInstallGeneration += 1;
|
|
792
1256
|
return YES;
|
|
793
1257
|
}
|
|
@@ -796,24 +1260,12 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
796
1260
|
if (_activeAnimationId == 0) return;
|
|
797
1261
|
const smoothclip::Presentation visible =
|
|
798
1262
|
[self smoothClipCurrentPresentation];
|
|
799
|
-
const CGRect visibleRect = CGRectMake(
|
|
800
|
-
visible.clip.x,
|
|
801
|
-
visible.clip.y,
|
|
802
|
-
visible.clip.width,
|
|
803
|
-
visible.clip.height);
|
|
804
|
-
const CGPoint visibleContentTranslation = CGPointMake(
|
|
805
|
-
visible.contentTranslateX,
|
|
806
|
-
visible.contentTranslateY);
|
|
807
1263
|
[self stopLayerAnimationWithoutCallback];
|
|
808
1264
|
if (useTarget) {
|
|
809
1265
|
[self applyRequestedClip];
|
|
810
1266
|
} else {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
_requestedContentTranslation = visibleContentTranslation;
|
|
814
|
-
[self writeLayerRect:visibleRect radius:visible.clip.radius];
|
|
815
|
-
[self writeContentTranslation:visibleContentTranslation];
|
|
816
|
-
[self syncVisibilityForRect:visibleRect];
|
|
1267
|
+
[self storeRequestedPresentation:visible];
|
|
1268
|
+
[self applyRequestedClip];
|
|
817
1269
|
}
|
|
818
1270
|
}
|
|
819
1271
|
|
|
@@ -824,6 +1276,8 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
824
1276
|
_activeAnimationId = 0;
|
|
825
1277
|
_activeAnimationKind = 0;
|
|
826
1278
|
_animationDelegate = nil;
|
|
1279
|
+
[_unequalCornerMask removeAnimationForKey:@"smoothClip.mask"];
|
|
1280
|
+
[self applyStaticCornerRepresentation:[self normalizedGeometryValue]];
|
|
827
1281
|
[self syncVisibilityForRect:_normalizedClip];
|
|
828
1282
|
smoothclip::viewAnimationDidStop(
|
|
829
1283
|
driverId, animationId, self, finished);
|
|
@@ -839,14 +1293,42 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
839
1293
|
isfinite(newProps.driverId) && newProps.driverId > 0
|
|
840
1294
|
? static_cast<uint64_t>(newProps.driverId)
|
|
841
1295
|
: 0;
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1296
|
+
smoothclip::Presentation initial{};
|
|
1297
|
+
if (newProps.presentationVersion == 2) {
|
|
1298
|
+
if (!SmoothClipBuildPresentationV2(
|
|
1299
|
+
newProps.initialClipX,
|
|
1300
|
+
newProps.initialClipY,
|
|
1301
|
+
newProps.initialClipWidth,
|
|
1302
|
+
newProps.initialClipHeight,
|
|
1303
|
+
newProps.initialClipTopLeftRadius,
|
|
1304
|
+
newProps.initialClipTopRightRadius,
|
|
1305
|
+
newProps.initialClipBottomRightRadius,
|
|
1306
|
+
newProps.initialClipBottomLeftRadius,
|
|
1307
|
+
newProps.initialClipCurve,
|
|
1308
|
+
newProps.initialContentTranslateX,
|
|
1309
|
+
newProps.initialContentTranslateY,
|
|
1310
|
+
newProps.initialContentScale,
|
|
1311
|
+
&initial)) {
|
|
1312
|
+
// The initial V2 presentation is one atomic value. Do not unregister an
|
|
1313
|
+
// old driver or apply a subset of valid channels when any field rejects.
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
} else if (newProps.presentationVersion == 0 ||
|
|
1317
|
+
newProps.presentationVersion == 1) {
|
|
1318
|
+
// Version zero is what old JS produces when the versioned prop does not
|
|
1319
|
+
// exist. Preserve that old-JS/new-native path byte-for-byte as V1.
|
|
1320
|
+
initial = {
|
|
1321
|
+
{newProps.initialClipX,
|
|
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.
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
850
1332
|
|
|
851
1333
|
if (nextDriverId != _driverId) {
|
|
852
1334
|
if (_driverId != 0) {
|
|
@@ -872,22 +1354,25 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
872
1354
|
|
|
873
1355
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
874
1356
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
|
|
875
|
-
CGFloat visibleRadius = 0;
|
|
876
1357
|
const BOOL wasAnimating = _activeAnimationId != 0;
|
|
877
1358
|
const BOOL wasPending = _pendingAnimationInstall;
|
|
878
1359
|
const uint32_t installGenerationAtEntry = _animationInstallGeneration;
|
|
879
1360
|
const smoothclip::Presentation visible = wasAnimating && !wasPending
|
|
880
1361
|
? [self smoothClipCurrentPresentation]
|
|
881
|
-
: smoothclip::Presentation{{0, 0, 0, 0, 0}, 0, 0};
|
|
1362
|
+
: smoothclip::Presentation{{0, 0, 0, 0, 0}, 0, 0, 1};
|
|
882
1363
|
const CGRect visibleRect = CGRectMake(
|
|
883
1364
|
visible.clip.x,
|
|
884
1365
|
visible.clip.y,
|
|
885
1366
|
visible.clip.width,
|
|
886
1367
|
visible.clip.height);
|
|
887
|
-
|
|
1368
|
+
const SmoothClipCornerRadii visibleRadii =
|
|
1369
|
+
SmoothClipPresentationRadii(visible.clip);
|
|
1370
|
+
const SmoothClipCornerCurve visibleCurve =
|
|
1371
|
+
SmoothClipPresentationCurve(visible.clip);
|
|
888
1372
|
const CGPoint visibleContentTranslation = CGPointMake(
|
|
889
1373
|
visible.contentTranslateX,
|
|
890
1374
|
visible.contentTranslateY);
|
|
1375
|
+
const CGFloat visibleContentScale = visible.contentScale;
|
|
891
1376
|
CFTimeInterval remaining = wasAnimating
|
|
892
1377
|
? MAX(0, _animationDuration -
|
|
893
1378
|
(CACurrentMediaTime() - _animationStartedAt))
|
|
@@ -943,23 +1428,29 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
943
1428
|
_timingAnimation = {remaining * 1000.0, 0, 0, 1, 1, 2};
|
|
944
1429
|
}
|
|
945
1430
|
SmoothNormalizedClipGeometry current;
|
|
946
|
-
|
|
1431
|
+
SmoothClipNormalizeGeometryV2(
|
|
947
1432
|
CGRectGetMinX(visibleRect),
|
|
948
1433
|
CGRectGetMinY(visibleRect),
|
|
949
1434
|
CGRectGetWidth(visibleRect),
|
|
950
1435
|
CGRectGetHeight(visibleRect),
|
|
951
|
-
|
|
1436
|
+
visibleRadii.topLeft,
|
|
1437
|
+
visibleRadii.topRight,
|
|
1438
|
+
visibleRadii.bottomRight,
|
|
1439
|
+
visibleRadii.bottomLeft,
|
|
1440
|
+
visibleCurve,
|
|
952
1441
|
self.bounds.size,
|
|
953
1442
|
¤t);
|
|
954
1443
|
SmoothNormalizedClipGeometry target;
|
|
955
1444
|
if ([self normalizedRequestedGeometry:&target]) {
|
|
956
|
-
[self
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
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];
|
|
963
1454
|
}
|
|
964
1455
|
} else {
|
|
965
1456
|
if (_driverId != 0 && smoothclip::hasActiveAnimation(_driverId)) {
|
|
@@ -974,31 +1465,27 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
974
1465
|
|
|
975
1466
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
|
|
976
1467
|
if (![super pointInside:point withEvent:event]) return NO;
|
|
977
|
-
|
|
1468
|
+
SmoothClipCornerRadii radii = _normalizedRadii;
|
|
978
1469
|
const CGRect clip = _activeAnimationId != 0
|
|
979
|
-
? [self
|
|
1470
|
+
? [self presentationRectWithRadii:&radii]
|
|
980
1471
|
: _normalizedClip;
|
|
981
|
-
if (_activeAnimationId == 0) radius = _normalizedRadius;
|
|
982
1472
|
if (CGRectIsEmpty(clip) || !CGRectContainsPoint(clip, point)) return NO;
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
return YES;
|
|
1473
|
+
CGPathRef path = nil;
|
|
1474
|
+
if (_clipContainer.layer.mask == _unequalCornerMask) {
|
|
1475
|
+
CAShapeLayer *mask = _activeAnimationId != 0
|
|
1476
|
+
? (CAShapeLayer *)_unequalCornerMask.presentationLayer
|
|
1477
|
+
: _unequalCornerMask;
|
|
1478
|
+
if (mask == nil) mask = _unequalCornerMask;
|
|
1479
|
+
path = mask.path;
|
|
1480
|
+
return path != nil && CGPathContainsPoint(path, NULL, point, NO);
|
|
992
1481
|
}
|
|
993
|
-
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
const CGFloat dy = point.y - centerY;
|
|
998
|
-
return dx * dx + dy * dy <= radius * radius;
|
|
1482
|
+
path = SmoothClipCreateRoundedRectPath(clip, radii, _normalizedCurve);
|
|
1483
|
+
const BOOL contains = CGPathContainsPoint(path, NULL, point, NO);
|
|
1484
|
+
CGPathRelease(path);
|
|
1485
|
+
return contains;
|
|
999
1486
|
}
|
|
1000
1487
|
|
|
1001
|
-
#pragma mark -
|
|
1488
|
+
#pragma mark - Native commands
|
|
1002
1489
|
|
|
1003
1490
|
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
|
|
1004
1491
|
RCTSmoothClipViewHandleCommand(self, commandName, args);
|
|
@@ -1028,6 +1515,39 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
1028
1515
|
contentTranslateY}];
|
|
1029
1516
|
}
|
|
1030
1517
|
|
|
1518
|
+
- (void)setClipPresentationV2:(double)x
|
|
1519
|
+
y:(double)y
|
|
1520
|
+
width:(double)width
|
|
1521
|
+
height:(double)height
|
|
1522
|
+
topLeftRadius:(double)topLeftRadius
|
|
1523
|
+
topRightRadius:(double)topRightRadius
|
|
1524
|
+
bottomRightRadius:(double)bottomRightRadius
|
|
1525
|
+
bottomLeftRadius:(double)bottomLeftRadius
|
|
1526
|
+
curveCode:(NSInteger)curveCode
|
|
1527
|
+
contentTranslateX:(double)contentTranslateX
|
|
1528
|
+
contentTranslateY:(double)contentTranslateY
|
|
1529
|
+
contentScale:(double)contentScale {
|
|
1530
|
+
smoothclip::Presentation presentation{};
|
|
1531
|
+
if (!SmoothClipBuildPresentationV2(
|
|
1532
|
+
x,
|
|
1533
|
+
y,
|
|
1534
|
+
width,
|
|
1535
|
+
height,
|
|
1536
|
+
topLeftRadius,
|
|
1537
|
+
topRightRadius,
|
|
1538
|
+
bottomRightRadius,
|
|
1539
|
+
bottomLeftRadius,
|
|
1540
|
+
curveCode,
|
|
1541
|
+
contentTranslateX,
|
|
1542
|
+
contentTranslateY,
|
|
1543
|
+
contentScale,
|
|
1544
|
+
&presentation)) {
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
_commandIsAuthoritative = YES;
|
|
1548
|
+
[self smoothClipApplyPresentation:presentation];
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1031
1551
|
- (void)prepareForRecycle {
|
|
1032
1552
|
if (_driverId != 0) {
|
|
1033
1553
|
smoothclip::unregisterView(_driverId, self);
|
|
@@ -1041,8 +1561,16 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
1041
1561
|
_normalizedClip = CGRectZero;
|
|
1042
1562
|
_requestedRadius = 0;
|
|
1043
1563
|
_normalizedRadius = 0;
|
|
1564
|
+
_requestedRadii = {0, 0, 0, 0};
|
|
1565
|
+
_normalizedRadii = {0, 0, 0, 0};
|
|
1566
|
+
_requestedCurve = SmoothClipCornerCurveCircular;
|
|
1567
|
+
_normalizedCurve = SmoothClipCornerCurveCircular;
|
|
1568
|
+
_requestedHasExplicitRadii = NO;
|
|
1569
|
+
_normalizedHasExplicitRadii = NO;
|
|
1044
1570
|
_requestedContentTranslation = CGPointZero;
|
|
1045
1571
|
_normalizedContentTranslation = CGPointZero;
|
|
1572
|
+
_requestedContentScale = 1;
|
|
1573
|
+
_normalizedContentScale = 1;
|
|
1046
1574
|
_hasLayout = NO;
|
|
1047
1575
|
_commandIsAuthoritative = NO;
|
|
1048
1576
|
|
|
@@ -1050,6 +1578,9 @@ static std::array<double, 7> SmoothClipVelocityChannels(
|
|
|
1050
1578
|
layer.bounds = CGRectZero;
|
|
1051
1579
|
layer.position = CGPointZero;
|
|
1052
1580
|
layer.cornerRadius = 0;
|
|
1581
|
+
layer.cornerCurve = kCACornerCurveCircular;
|
|
1582
|
+
layer.mask = nil;
|
|
1583
|
+
_unequalCornerMask.path = nil;
|
|
1053
1584
|
_contentContainer.layer.affineTransform = CGAffineTransformIdentity;
|
|
1054
1585
|
[self setClipContainerHidden:YES];
|
|
1055
1586
|
}
|