react-native-smooth-clip-view 0.2.9 → 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 -349
- package/android/build.gradle +8 -4
- package/android/proguard-rules.pro +3 -3
- package/android/src/main/cpp/SmoothClipAndroid.h +13 -6
- package/android/src/main/cpp/SmoothClipBindings.cpp +605 -123
- package/android/src/main/cpp/SmoothClipRegistry.cpp +975 -209
- package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +192 -118
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +22 -4
- package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +81 -56
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +449 -240
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +150 -38
- package/cpp/SmoothClipAnimationCurve.h +317 -397
- package/cpp/SmoothClipRegistry.h +84 -20
- package/cpp/SmoothClipSharedGeometry.h +131 -22
- package/cpp/SmoothClipVelocityTracker.h +16 -12
- package/ios/SmoothClipGeometry.h +188 -21
- package/ios/SmoothClipRegistry.mm +869 -368
- package/ios/SmoothClipRegistrySnapshot.h +13 -0
- package/ios/SmoothClipTurboModule.cpp +513 -156
- package/ios/SmoothClipTurboModule.h +123 -56
- package/ios/SmoothClipView.mm +1074 -449
- package/ios/SmoothClipViewRegistry.h +3 -7
- 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 +94 -11
- 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 +32 -12
- package/lib/module/capabilities.js +4 -0
- package/lib/module/capabilities.js.map +1 -0
- package/lib/module/capabilities.native.js +18 -0
- package/lib/module/capabilities.native.js.map +1 -0
- package/lib/module/capabilityTypes.js +2 -0
- package/lib/module/capabilityTypes.js.map +1 -0
- 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 +180 -30
- package/lib/module/geometry.js.map +1 -1
- package/lib/module/groupTypes.js +4 -0
- package/lib/module/groupTypes.js.map +1 -0
- 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 +4 -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 +20 -4
- 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 +139 -4
- package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +16 -3
- 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 +4 -0
- package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
- 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 +49 -5
- 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 +8 -5
- 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 +17 -13
- package/src/NativeSmoothClipModule.ts +80 -56
- package/src/SmoothClipView.ios.tsx +142 -16
- package/src/SmoothClipView.tsx +6 -76
- package/src/SmoothClipViewNativeComponent.ts +32 -12
- package/src/capabilities.native.ts +19 -0
- package/src/capabilities.ts +2 -0
- package/src/capabilityTypes.ts +3 -0
- 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 +293 -45
- 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 +32 -12
- package/src/nativeCompletion.ts +67 -32
- package/src/presentationCodec.ts +134 -0
- package/lib/module/SmoothClipView.web.js +0 -54
- package/lib/module/SmoothClipView.web.js.map +0 -1
- package/lib/module/driverState.js +0 -110
- 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 -284
- package/lib/module/drivers.js.map +0 -1
- package/lib/module/drivers.native.js +0 -556
- package/lib/module/drivers.native.js.map +0 -1
- package/lib/module/index.web.js +0 -8
- package/lib/module/index.web.js.map +0 -1
- package/lib/module/reactRequests.js +0 -56
- package/lib/module/reactRequests.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/driverState.d.ts +0 -31
- package/lib/typescript/src/driverState.d.ts.map +0 -1
- package/lib/typescript/src/driverTypes.d.ts +0 -107
- 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 -5
- 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/index.web.d.ts +0 -9
- package/lib/typescript/src/index.web.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/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/driverState.ts +0 -158
- package/src/driverTypes.ts +0 -140
- package/src/drivers.android.ts +0 -8
- package/src/drivers.ios.ts +0 -8
- package/src/drivers.native.ts +0 -899
- package/src/drivers.ts +0 -427
- package/src/index.web.ts +0 -23
- package/src/reactRequests.ts +0 -76
- package/src/webClipPath.ts +0 -30
package/src/geometry.ts
CHANGED
|
@@ -1,42 +1,186 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise -- React Native colors are converted to RRGGBBAA */
|
|
2
|
+
import type { ColorValue } from 'react-native';
|
|
3
|
+
import { processColor } from 'react-native-reanimated';
|
|
4
|
+
|
|
5
|
+
export type ClipCurve = 'circular' | 'continuous';
|
|
6
|
+
|
|
7
|
+
/** One numeric outset shadow following React Native's `boxShadow` field names. */
|
|
8
|
+
export type ClipBoxShadow = Readonly<{
|
|
9
|
+
color?: ColorValue;
|
|
10
|
+
offsetX: number;
|
|
11
|
+
offsetY: number;
|
|
12
|
+
/** CSS box-shadow blur radius. iOS converts this to CALayer radius / 2. */
|
|
13
|
+
blurRadius?: number;
|
|
14
|
+
spreadDistance?: number;
|
|
15
|
+
}>;
|
|
16
|
+
|
|
17
|
+
export type CanonicalClipBoxShadow = Readonly<{
|
|
18
|
+
/** React Native numeric RRGGBBAA color; canonicalization is idempotent. */
|
|
19
|
+
color: ColorValue;
|
|
20
|
+
offsetX: number;
|
|
21
|
+
offsetY: number;
|
|
22
|
+
blurRadius: number;
|
|
23
|
+
spreadDistance: number;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
1
26
|
export type ClipGeometry = Readonly<{
|
|
2
27
|
x: number;
|
|
3
28
|
y: number;
|
|
4
29
|
width: number;
|
|
5
30
|
height: number;
|
|
31
|
+
/** Default for every corner without an explicit override. */
|
|
6
32
|
radius: number;
|
|
33
|
+
topLeftRadius?: number;
|
|
34
|
+
topRightRadius?: number;
|
|
35
|
+
bottomRightRadius?: number;
|
|
36
|
+
bottomLeftRadius?: number;
|
|
37
|
+
curve?: ClipCurve;
|
|
7
38
|
}>;
|
|
8
39
|
|
|
9
|
-
export type
|
|
40
|
+
export type CanonicalClipGeometry = Readonly<{
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
10
43
|
width: number;
|
|
11
44
|
height: number;
|
|
45
|
+
/** Canonical all-corners value; zero when the corner radii differ. */
|
|
46
|
+
radius: number;
|
|
47
|
+
topLeftRadius: number;
|
|
48
|
+
topRightRadius: number;
|
|
49
|
+
bottomRightRadius: number;
|
|
50
|
+
bottomLeftRadius: number;
|
|
51
|
+
curve: ClipCurve;
|
|
12
52
|
}>;
|
|
13
53
|
|
|
14
54
|
export type SmoothClipPresentation = Readonly<{
|
|
15
55
|
clip: ClipGeometry;
|
|
16
56
|
contentTranslateX: number;
|
|
17
57
|
contentTranslateY: number;
|
|
58
|
+
/** Defaults to 1 when omitted. */
|
|
59
|
+
contentScale?: number;
|
|
60
|
+
boxShadow?: ClipBoxShadow;
|
|
18
61
|
}>;
|
|
19
62
|
|
|
20
|
-
export
|
|
21
|
-
clip:
|
|
22
|
-
contentTranslateX
|
|
23
|
-
contentTranslateY
|
|
24
|
-
|
|
63
|
+
export type CanonicalSmoothClipPresentation = Readonly<{
|
|
64
|
+
clip: CanonicalClipGeometry;
|
|
65
|
+
contentTranslateX: number;
|
|
66
|
+
contentTranslateY: number;
|
|
67
|
+
contentScale: number;
|
|
68
|
+
boxShadow?: CanonicalClipBoxShadow;
|
|
69
|
+
}>;
|
|
70
|
+
|
|
71
|
+
const DEFAULT_SHADOW_COLOR = '#000000ff';
|
|
72
|
+
|
|
73
|
+
function isClipCurve(curve: ClipCurve | undefined): boolean {
|
|
74
|
+
'worklet';
|
|
75
|
+
return curve === undefined || curve === 'circular' || curve === 'continuous';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isFiniteOptionalNumber(value: number | undefined): boolean {
|
|
25
79
|
'worklet';
|
|
26
|
-
return
|
|
80
|
+
return value === undefined || Number.isFinite(value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function canonicalizeClipBoxShadow(
|
|
84
|
+
shadow: ClipBoxShadow | undefined
|
|
85
|
+
): CanonicalClipBoxShadow | null | undefined {
|
|
86
|
+
'worklet';
|
|
87
|
+
if (shadow === undefined) return undefined;
|
|
88
|
+
const { offsetX, offsetY } = shadow;
|
|
89
|
+
const blurRadius = shadow.blurRadius ?? 0;
|
|
90
|
+
const spreadDistance = shadow.spreadDistance ?? 0;
|
|
91
|
+
if (
|
|
92
|
+
!Number.isFinite(offsetX) ||
|
|
93
|
+
!Number.isFinite(offsetY) ||
|
|
94
|
+
!Number.isFinite(blurRadius) ||
|
|
95
|
+
!Number.isFinite(spreadDistance)
|
|
96
|
+
) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const processedColor =
|
|
100
|
+
shadow.color === undefined
|
|
101
|
+
? processColor(DEFAULT_SHADOW_COLOR)
|
|
102
|
+
: processColor(shadow.color);
|
|
103
|
+
// Platform and dynamic colors cannot be represented by the scalar native
|
|
104
|
+
// animation protocol. Callers can resolve those colors before presenting.
|
|
105
|
+
if (typeof processedColor !== 'number') return null;
|
|
106
|
+
const unsignedProcessedColor = processedColor >>> 0;
|
|
107
|
+
const color =
|
|
108
|
+
((unsignedProcessedColor << 8) | (unsignedProcessedColor >>> 24)) >>> 0;
|
|
109
|
+
return {
|
|
110
|
+
color: color as unknown as ColorValue,
|
|
111
|
+
offsetX,
|
|
112
|
+
offsetY,
|
|
113
|
+
blurRadius: Math.max(0, blurRadius),
|
|
114
|
+
spreadDistance,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function clipBoxShadowEquals(
|
|
119
|
+
first: ClipBoxShadow | undefined,
|
|
120
|
+
second: ClipBoxShadow | undefined
|
|
121
|
+
): boolean {
|
|
122
|
+
'worklet';
|
|
123
|
+
if (first === undefined || second === undefined) return first === second;
|
|
124
|
+
const canonicalFirst = canonicalizeClipBoxShadow(first);
|
|
125
|
+
const canonicalSecond = canonicalizeClipBoxShadow(second);
|
|
126
|
+
return (
|
|
127
|
+
canonicalFirst !== null &&
|
|
128
|
+
canonicalFirst !== undefined &&
|
|
129
|
+
canonicalSecond !== null &&
|
|
130
|
+
canonicalSecond !== undefined &&
|
|
131
|
+
canonicalFirst.color === canonicalSecond.color &&
|
|
132
|
+
canonicalFirst.offsetX === canonicalSecond.offsetX &&
|
|
133
|
+
canonicalFirst.offsetY === canonicalSecond.offsetY &&
|
|
134
|
+
canonicalFirst.blurRadius === canonicalSecond.blurRadius &&
|
|
135
|
+
canonicalFirst.spreadDistance === canonicalSecond.spreadDistance
|
|
136
|
+
);
|
|
27
137
|
}
|
|
28
138
|
|
|
29
139
|
export function isFiniteClipGeometry(geometry: ClipGeometry): boolean {
|
|
30
140
|
'worklet';
|
|
141
|
+
const width = Math.max(0, geometry.width);
|
|
142
|
+
const height = Math.max(0, geometry.height);
|
|
31
143
|
return (
|
|
32
144
|
Number.isFinite(geometry.x) &&
|
|
33
145
|
Number.isFinite(geometry.y) &&
|
|
34
146
|
Number.isFinite(geometry.width) &&
|
|
35
147
|
Number.isFinite(geometry.height) &&
|
|
36
|
-
Number.isFinite(geometry.radius)
|
|
148
|
+
Number.isFinite(geometry.radius) &&
|
|
149
|
+
Number.isFinite(geometry.x + width) &&
|
|
150
|
+
Number.isFinite(geometry.y + height) &&
|
|
151
|
+
isFiniteOptionalNumber(geometry.topLeftRadius) &&
|
|
152
|
+
isFiniteOptionalNumber(geometry.topRightRadius) &&
|
|
153
|
+
isFiniteOptionalNumber(geometry.bottomRightRadius) &&
|
|
154
|
+
isFiniteOptionalNumber(geometry.bottomLeftRadius) &&
|
|
155
|
+
isClipCurve(geometry.curve)
|
|
37
156
|
);
|
|
38
157
|
}
|
|
39
158
|
|
|
159
|
+
function resolvedTopLeftRadius(geometry: ClipGeometry): number {
|
|
160
|
+
'worklet';
|
|
161
|
+
return geometry.topLeftRadius ?? geometry.radius;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function resolvedTopRightRadius(geometry: ClipGeometry): number {
|
|
165
|
+
'worklet';
|
|
166
|
+
return geometry.topRightRadius ?? geometry.radius;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function resolvedBottomRightRadius(geometry: ClipGeometry): number {
|
|
170
|
+
'worklet';
|
|
171
|
+
return geometry.bottomRightRadius ?? geometry.radius;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function resolvedBottomLeftRadius(geometry: ClipGeometry): number {
|
|
175
|
+
'worklet';
|
|
176
|
+
return geometry.bottomLeftRadius ?? geometry.radius;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function resolvedCurve(geometry: ClipGeometry): ClipCurve {
|
|
180
|
+
'worklet';
|
|
181
|
+
return geometry.curve ?? 'circular';
|
|
182
|
+
}
|
|
183
|
+
|
|
40
184
|
export function clipGeometryEquals(
|
|
41
185
|
first: ClipGeometry | null,
|
|
42
186
|
second: ClipGeometry
|
|
@@ -48,18 +192,33 @@ export function clipGeometryEquals(
|
|
|
48
192
|
first.y === second.y &&
|
|
49
193
|
first.width === second.width &&
|
|
50
194
|
first.height === second.height &&
|
|
51
|
-
first.radius === second.radius
|
|
195
|
+
first.radius === second.radius &&
|
|
196
|
+
resolvedTopLeftRadius(first) === resolvedTopLeftRadius(second) &&
|
|
197
|
+
resolvedTopRightRadius(first) === resolvedTopRightRadius(second) &&
|
|
198
|
+
resolvedBottomRightRadius(first) === resolvedBottomRightRadius(second) &&
|
|
199
|
+
resolvedBottomLeftRadius(first) === resolvedBottomLeftRadius(second) &&
|
|
200
|
+
resolvedCurve(first) === resolvedCurve(second)
|
|
52
201
|
);
|
|
53
202
|
}
|
|
54
203
|
|
|
204
|
+
function resolvedContentScale(presentation: SmoothClipPresentation): number {
|
|
205
|
+
'worklet';
|
|
206
|
+
return presentation.contentScale ?? 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
55
209
|
export function isFiniteClipPresentation(
|
|
56
210
|
presentation: SmoothClipPresentation
|
|
57
211
|
): boolean {
|
|
58
212
|
'worklet';
|
|
213
|
+
const contentScale = resolvedContentScale(presentation);
|
|
214
|
+
const shadow = canonicalizeClipBoxShadow(presentation.boxShadow);
|
|
59
215
|
return (
|
|
60
216
|
isFiniteClipGeometry(presentation.clip) &&
|
|
61
217
|
Number.isFinite(presentation.contentTranslateX) &&
|
|
62
|
-
Number.isFinite(presentation.contentTranslateY)
|
|
218
|
+
Number.isFinite(presentation.contentTranslateY) &&
|
|
219
|
+
Number.isFinite(contentScale) &&
|
|
220
|
+
contentScale > 0 &&
|
|
221
|
+
shadow !== null
|
|
63
222
|
);
|
|
64
223
|
}
|
|
65
224
|
|
|
@@ -72,51 +231,140 @@ export function clipPresentationEquals(
|
|
|
72
231
|
first !== null &&
|
|
73
232
|
clipGeometryEquals(first.clip, second.clip) &&
|
|
74
233
|
first.contentTranslateX === second.contentTranslateX &&
|
|
75
|
-
first.contentTranslateY === second.contentTranslateY
|
|
234
|
+
first.contentTranslateY === second.contentTranslateY &&
|
|
235
|
+
resolvedContentScale(first) === resolvedContentScale(second) &&
|
|
236
|
+
clipBoxShadowEquals(first.boxShadow, second.boxShadow)
|
|
76
237
|
);
|
|
77
238
|
}
|
|
78
239
|
|
|
240
|
+
function createCanonicalClipGeometry(
|
|
241
|
+
geometry: ClipGeometry,
|
|
242
|
+
x: number,
|
|
243
|
+
y: number,
|
|
244
|
+
width: number,
|
|
245
|
+
height: number
|
|
246
|
+
): CanonicalClipGeometry {
|
|
247
|
+
'worklet';
|
|
248
|
+
const topLeftRadius = Math.max(0, resolvedTopLeftRadius(geometry));
|
|
249
|
+
const topRightRadius = Math.max(0, resolvedTopRightRadius(geometry));
|
|
250
|
+
const bottomRightRadius = Math.max(0, resolvedBottomRightRadius(geometry));
|
|
251
|
+
const bottomLeftRadius = Math.max(0, resolvedBottomLeftRadius(geometry));
|
|
252
|
+
|
|
253
|
+
// CSS corner-overlap reduction uses one common factor so all four corners
|
|
254
|
+
// retain their proportions when either pair exceeds the available edge.
|
|
255
|
+
let scale = 1;
|
|
256
|
+
const top = topLeftRadius + topRightRadius;
|
|
257
|
+
const right = topRightRadius + bottomRightRadius;
|
|
258
|
+
const bottom = bottomLeftRadius + bottomRightRadius;
|
|
259
|
+
const left = topLeftRadius + bottomLeftRadius;
|
|
260
|
+
|
|
261
|
+
if (top > 0) scale = Math.min(scale, width / top);
|
|
262
|
+
if (right > 0) scale = Math.min(scale, height / right);
|
|
263
|
+
if (bottom > 0) scale = Math.min(scale, width / bottom);
|
|
264
|
+
if (left > 0) scale = Math.min(scale, height / left);
|
|
265
|
+
scale = Math.max(0, Math.min(1, scale));
|
|
266
|
+
|
|
267
|
+
const canonicalTopLeftRadius = topLeftRadius * scale;
|
|
268
|
+
const canonicalTopRightRadius = topRightRadius * scale;
|
|
269
|
+
const canonicalBottomRightRadius = bottomRightRadius * scale;
|
|
270
|
+
const canonicalBottomLeftRadius = bottomLeftRadius * scale;
|
|
271
|
+
const radius =
|
|
272
|
+
canonicalTopLeftRadius === canonicalTopRightRadius &&
|
|
273
|
+
canonicalTopRightRadius === canonicalBottomRightRadius &&
|
|
274
|
+
canonicalBottomRightRadius === canonicalBottomLeftRadius
|
|
275
|
+
? canonicalTopLeftRadius
|
|
276
|
+
: 0;
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
x,
|
|
280
|
+
y,
|
|
281
|
+
width,
|
|
282
|
+
height,
|
|
283
|
+
radius,
|
|
284
|
+
topLeftRadius: canonicalTopLeftRadius,
|
|
285
|
+
topRightRadius: canonicalTopRightRadius,
|
|
286
|
+
bottomRightRadius: canonicalBottomRightRadius,
|
|
287
|
+
bottomLeftRadius: canonicalBottomLeftRadius,
|
|
288
|
+
curve: resolvedCurve(geometry),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
79
292
|
/**
|
|
80
|
-
*
|
|
81
|
-
* Native remains authoritative because its bounds are the actual rendered size.
|
|
293
|
+
* Expands presentation defaults and applies CSS proportional corner scaling.
|
|
82
294
|
*/
|
|
83
|
-
export function
|
|
84
|
-
geometry: ClipGeometry
|
|
85
|
-
|
|
86
|
-
): ClipGeometry | null {
|
|
295
|
+
export function canonicalizeClipGeometry(
|
|
296
|
+
geometry: ClipGeometry
|
|
297
|
+
): CanonicalClipGeometry | null {
|
|
87
298
|
'worklet';
|
|
88
|
-
|
|
299
|
+
if (!isFiniteClipGeometry(geometry)) return null;
|
|
300
|
+
|
|
301
|
+
const width = Math.max(0, geometry.width);
|
|
302
|
+
const height = Math.max(0, geometry.height);
|
|
303
|
+
return createCanonicalClipGeometry(
|
|
304
|
+
geometry,
|
|
89
305
|
geometry.x,
|
|
90
306
|
geometry.y,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
geometry.radius,
|
|
94
|
-
bounds.width,
|
|
95
|
-
bounds.height,
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
if (!values.every(Number.isFinite)) return null;
|
|
99
|
-
|
|
100
|
-
const boundsWidth = Math.max(0, bounds.width);
|
|
101
|
-
const boundsHeight = Math.max(0, bounds.height);
|
|
102
|
-
const requestedWidth = Math.max(0, geometry.width);
|
|
103
|
-
const requestedHeight = Math.max(0, geometry.height);
|
|
104
|
-
|
|
105
|
-
const left = Math.min(boundsWidth, Math.max(0, geometry.x));
|
|
106
|
-
const top = Math.min(boundsHeight, Math.max(0, geometry.y));
|
|
107
|
-
const right = Math.min(boundsWidth, Math.max(0, geometry.x + requestedWidth));
|
|
108
|
-
const bottom = Math.min(
|
|
109
|
-
boundsHeight,
|
|
110
|
-
Math.max(0, geometry.y + requestedHeight)
|
|
307
|
+
width,
|
|
308
|
+
height
|
|
111
309
|
);
|
|
112
|
-
|
|
113
|
-
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function canonicalizeClipPresentation(
|
|
313
|
+
presentation: SmoothClipPresentation
|
|
314
|
+
): CanonicalSmoothClipPresentation | null {
|
|
315
|
+
'worklet';
|
|
316
|
+
if (!isFiniteClipPresentation(presentation)) return null;
|
|
317
|
+
|
|
318
|
+
const clip = canonicalizeClipGeometry(presentation.clip);
|
|
319
|
+
if (clip === null) return null;
|
|
320
|
+
const boxShadow = canonicalizeClipBoxShadow(presentation.boxShadow);
|
|
321
|
+
if (boxShadow === null) return null;
|
|
114
322
|
|
|
115
323
|
return {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
324
|
+
clip,
|
|
325
|
+
contentTranslateX: presentation.contentTranslateX,
|
|
326
|
+
contentTranslateY: presentation.contentTranslateY,
|
|
327
|
+
contentScale: resolvedContentScale(presentation),
|
|
328
|
+
...(boxShadow === undefined ? {} : { boxShadow }),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Keep this below the canonicalization helpers so the worklet never closes
|
|
333
|
+
// over a helper declared later in this module.
|
|
334
|
+
export function createClipPresentation(
|
|
335
|
+
clip: ClipGeometry,
|
|
336
|
+
contentTranslateX = 0,
|
|
337
|
+
contentTranslateY = 0,
|
|
338
|
+
contentScale = 1,
|
|
339
|
+
boxShadow?: ClipBoxShadow
|
|
340
|
+
): CanonicalSmoothClipPresentation {
|
|
341
|
+
'worklet';
|
|
342
|
+
const canonicalClip = canonicalizeClipGeometry(clip);
|
|
343
|
+
const canonicalShadow = canonicalizeClipBoxShadow(boxShadow);
|
|
344
|
+
return {
|
|
345
|
+
// Keep invalid input observably invalid so downstream validation remains
|
|
346
|
+
// atomic instead of accidentally sanitizing a required scalar.
|
|
347
|
+
clip: canonicalClip ?? {
|
|
348
|
+
x: clip.x,
|
|
349
|
+
y: clip.y,
|
|
350
|
+
width: clip.width,
|
|
351
|
+
height: clip.height,
|
|
352
|
+
radius: clip.radius,
|
|
353
|
+
topLeftRadius: resolvedTopLeftRadius(clip),
|
|
354
|
+
topRightRadius: resolvedTopRightRadius(clip),
|
|
355
|
+
bottomRightRadius: resolvedBottomRightRadius(clip),
|
|
356
|
+
bottomLeftRadius: resolvedBottomLeftRadius(clip),
|
|
357
|
+
curve: resolvedCurve(clip),
|
|
358
|
+
},
|
|
359
|
+
contentTranslateX,
|
|
360
|
+
contentTranslateY,
|
|
361
|
+
contentScale,
|
|
362
|
+
...(canonicalShadow === undefined
|
|
363
|
+
? {}
|
|
364
|
+
: {
|
|
365
|
+
// Match the geometry constructor's invalid-input behavior: retain the
|
|
366
|
+
// bad source value so the driver's atomic validation rejects it.
|
|
367
|
+
boxShadow: (canonicalShadow ?? boxShadow) as CanonicalClipBoxShadow,
|
|
368
|
+
}),
|
|
121
369
|
};
|
|
122
370
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SmoothClipAnimation,
|
|
3
|
+
SmoothClipCompletion,
|
|
4
|
+
SmoothClipReactRun,
|
|
5
|
+
SmoothClipRef,
|
|
6
|
+
SmoothClipRunHandle,
|
|
7
|
+
} from './controllerTypes';
|
|
8
|
+
import type {
|
|
9
|
+
CanonicalSmoothClipPresentation,
|
|
10
|
+
SmoothClipPresentation,
|
|
11
|
+
} from './geometry';
|
|
12
|
+
|
|
13
|
+
export type SmoothClipGroupFrame = Readonly<{
|
|
14
|
+
clip: SmoothClipRef;
|
|
15
|
+
frame: SmoothClipPresentation;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export type SmoothClipGroupTarget = Readonly<{
|
|
19
|
+
clip: SmoothClipRef;
|
|
20
|
+
target: SmoothClipPresentation;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
export type SmoothClipGroupSnapshot = Readonly<{
|
|
24
|
+
clip: SmoothClipRef;
|
|
25
|
+
frame: CanonicalSmoothClipPresentation;
|
|
26
|
+
ready: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
|
|
29
|
+
export type SmoothClipGroupOptions = Readonly<{
|
|
30
|
+
/** Stable React-runtime callback for UI- and React-runtime transactions. */
|
|
31
|
+
onAnimationComplete?: (result: SmoothClipCompletion) => void;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export type SmoothClipGroup = Readonly<{
|
|
35
|
+
ui: Readonly<{
|
|
36
|
+
setFrames(entries: readonly SmoothClipGroupFrame[]): void;
|
|
37
|
+
beginInteraction(
|
|
38
|
+
clips: readonly SmoothClipRef[]
|
|
39
|
+
): readonly SmoothClipGroupSnapshot[];
|
|
40
|
+
animateTo(
|
|
41
|
+
entries: readonly SmoothClipGroupTarget[],
|
|
42
|
+
animation: SmoothClipAnimation,
|
|
43
|
+
completionTag?: number
|
|
44
|
+
): SmoothClipRunHandle | null;
|
|
45
|
+
cancel(run: SmoothClipRunHandle): readonly SmoothClipGroupSnapshot[];
|
|
46
|
+
}>;
|
|
47
|
+
react: Readonly<{
|
|
48
|
+
animateTo(
|
|
49
|
+
entries: readonly SmoothClipGroupTarget[],
|
|
50
|
+
animation: SmoothClipAnimation
|
|
51
|
+
): SmoothClipReactRun;
|
|
52
|
+
}>;
|
|
53
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSmoothClipGroup } from './groups.native';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSmoothClipGroup } from './groups.native';
|