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
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
package com.smoothclipview
|
|
2
2
|
|
|
3
|
+
import android.graphics.Path
|
|
3
4
|
import android.view.View
|
|
4
5
|
import kotlin.math.max
|
|
5
6
|
import kotlin.math.min
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
internal const val CLIP_CURVE_CIRCULAR = 0
|
|
9
|
+
internal const val CLIP_CURVE_CONTINUOUS = 1
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* Canonicalizes raw clip geometry without allocating a result object. Host
|
|
13
|
+
* bounds deliberately do not participate; SmoothClipView crops at draw time.
|
|
11
14
|
*/
|
|
12
|
-
internal inline fun
|
|
15
|
+
internal inline fun canonicalizeClipGeometryPx(
|
|
13
16
|
x: Float,
|
|
14
17
|
y: Float,
|
|
15
18
|
width: Float,
|
|
16
19
|
height: Float,
|
|
17
20
|
radius: Float,
|
|
18
|
-
hostWidth: Float,
|
|
19
|
-
hostHeight: Float,
|
|
20
21
|
apply: (
|
|
21
22
|
left: Float,
|
|
22
23
|
top: Float,
|
|
@@ -26,107 +27,209 @@ internal inline fun normalizeClipGeometryPx(
|
|
|
26
27
|
) -> Unit,
|
|
27
28
|
): Boolean {
|
|
28
29
|
if (!x.isFinite() || !y.isFinite() || !width.isFinite() ||
|
|
29
|
-
!height.isFinite() || !radius.isFinite()
|
|
30
|
-
!hostWidth.isFinite() || !hostHeight.isFinite()
|
|
30
|
+
!height.isFinite() || !radius.isFinite()
|
|
31
31
|
) {
|
|
32
32
|
return false
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
val
|
|
36
|
-
val
|
|
37
|
-
val
|
|
38
|
-
val
|
|
39
|
-
|
|
40
|
-
val
|
|
41
|
-
val right = requestedRight.coerceIn(0f, boundedHostWidth)
|
|
42
|
-
val bottom = requestedBottom.coerceIn(0f, boundedHostHeight)
|
|
43
|
-
val visibleWidth = max(0f, right - left)
|
|
44
|
-
val visibleHeight = max(0f, bottom - top)
|
|
45
|
-
val normalizedRadius = min(
|
|
35
|
+
val canonicalWidth = max(0f, width)
|
|
36
|
+
val canonicalHeight = max(0f, height)
|
|
37
|
+
val right = x + canonicalWidth
|
|
38
|
+
val bottom = y + canonicalHeight
|
|
39
|
+
if (!right.isFinite() || !bottom.isFinite()) return false
|
|
40
|
+
val canonicalRadius = min(
|
|
46
41
|
radius.coerceAtLeast(0f),
|
|
47
|
-
min(
|
|
42
|
+
min(canonicalWidth, canonicalHeight) / 2f,
|
|
48
43
|
)
|
|
49
44
|
|
|
50
|
-
apply(
|
|
45
|
+
apply(x, y, right, bottom, canonicalRadius)
|
|
51
46
|
return true
|
|
52
47
|
}
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Canonicalization with independent radii. Radii use the CSS overlap rule:
|
|
51
|
+
* one shared factor keeps all four corners proportional when any opposing pair
|
|
52
|
+
* exceeds the requested width or height. This mirrors
|
|
53
|
+
* SmoothClipCanonicalize in the shared C++ delivery path.
|
|
54
|
+
*/
|
|
55
|
+
internal inline fun canonicalizeClipGeometryPx(
|
|
56
|
+
x: Float,
|
|
57
|
+
y: Float,
|
|
58
|
+
width: Float,
|
|
59
|
+
height: Float,
|
|
60
|
+
topLeftRadius: Float,
|
|
61
|
+
topRightRadius: Float,
|
|
62
|
+
bottomRightRadius: Float,
|
|
63
|
+
bottomLeftRadius: Float,
|
|
64
|
+
curveCode: Int,
|
|
65
|
+
apply: (
|
|
66
|
+
left: Float,
|
|
67
|
+
top: Float,
|
|
68
|
+
right: Float,
|
|
69
|
+
bottom: Float,
|
|
70
|
+
canonicalTopLeftRadius: Float,
|
|
71
|
+
canonicalTopRightRadius: Float,
|
|
72
|
+
canonicalBottomRightRadius: Float,
|
|
73
|
+
canonicalBottomLeftRadius: Float,
|
|
74
|
+
canonicalCurveCode: Int,
|
|
75
|
+
) -> Unit,
|
|
76
|
+
): Boolean {
|
|
77
|
+
if (!x.isFinite() || !y.isFinite() || !width.isFinite() ||
|
|
78
|
+
!height.isFinite() || !topLeftRadius.isFinite() ||
|
|
79
|
+
!topRightRadius.isFinite() || !bottomRightRadius.isFinite() ||
|
|
80
|
+
!bottomLeftRadius.isFinite() ||
|
|
81
|
+
(curveCode != CLIP_CURVE_CIRCULAR && curveCode != CLIP_CURVE_CONTINUOUS)
|
|
82
|
+
) {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
val canonicalWidth = max(0f, width)
|
|
87
|
+
val canonicalHeight = max(0f, height)
|
|
88
|
+
val right = x + canonicalWidth
|
|
89
|
+
val bottom = y + canonicalHeight
|
|
90
|
+
if (!right.isFinite() || !bottom.isFinite()) return false
|
|
91
|
+
val topLeft = topLeftRadius.coerceAtLeast(0f)
|
|
92
|
+
val topRight = topRightRadius.coerceAtLeast(0f)
|
|
93
|
+
val bottomRight = bottomRightRadius.coerceAtLeast(0f)
|
|
94
|
+
val bottomLeft = bottomLeftRadius.coerceAtLeast(0f)
|
|
95
|
+
|
|
96
|
+
var radiusScale = 1f
|
|
97
|
+
val topWidth = topLeft + topRight
|
|
98
|
+
val bottomWidth = bottomLeft + bottomRight
|
|
99
|
+
val leftHeight = topLeft + bottomLeft
|
|
100
|
+
val rightHeight = topRight + bottomRight
|
|
101
|
+
if (topWidth > 0f) radiusScale = min(radiusScale, canonicalWidth / topWidth)
|
|
102
|
+
if (bottomWidth > 0f) radiusScale = min(radiusScale, canonicalWidth / bottomWidth)
|
|
103
|
+
if (leftHeight > 0f) radiusScale = min(radiusScale, canonicalHeight / leftHeight)
|
|
104
|
+
if (rightHeight > 0f) radiusScale = min(radiusScale, canonicalHeight / rightHeight)
|
|
105
|
+
radiusScale = radiusScale.coerceIn(0f, 1f)
|
|
56
106
|
|
|
57
|
-
|
|
58
|
-
|
|
107
|
+
apply(
|
|
108
|
+
x,
|
|
109
|
+
y,
|
|
110
|
+
right,
|
|
111
|
+
bottom,
|
|
112
|
+
topLeft * radiusScale,
|
|
113
|
+
topRight * radiusScale,
|
|
114
|
+
bottomRight * radiusScale,
|
|
115
|
+
bottomLeft * radiusScale,
|
|
116
|
+
curveCode,
|
|
117
|
+
)
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
59
120
|
|
|
60
121
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* content inside translates in floats. That oscillation is what reads as
|
|
68
|
-
* shimmer on the slow tail of an animation, where the per-frame delta drops
|
|
69
|
-
* below a pixel and the rounding becomes the motion.
|
|
70
|
-
*
|
|
71
|
-
* An int rect cannot hold origin, far edge and extent within half a pixel at
|
|
72
|
-
* once (extent = far - origin, so bounding both edges at 0.5 bounds the extent
|
|
73
|
-
* at 1.0); the only choice is which error oscillates. Deriving the far edge
|
|
74
|
-
* makes the emitted extent a pure function of the true extent — stable, error
|
|
75
|
-
* <= 0.5 px — and moves the slack onto the far edge, <= 1.0 px and static. A
|
|
76
|
-
* static bias does not read as jank; an alternating one does.
|
|
77
|
-
*
|
|
78
|
-
* Two consequences, both benign:
|
|
79
|
-
* - The far edge can overshoot the host bound by up to 1 px instead of 0.5. The
|
|
80
|
-
* view's own bounds already clip, so an over-large outline just means that
|
|
81
|
-
* edge is not clipped that frame — which independent rounding already
|
|
82
|
-
* produced whenever `round(far)` rounded up.
|
|
83
|
-
* - A sub-pixel extent (0 < extent < 0.5) rounds to a degenerate rect, which
|
|
84
|
-
* `Outline.setRoundRect` collapses to `setEmpty()`. Independent rounding hit
|
|
85
|
-
* that state on some frames and a 1 px rect on others, i.e. it flickered;
|
|
86
|
-
* deriving the far edge makes it consistent. The emitted integer rect also
|
|
87
|
-
* governs visibility, accessibility and hit testing, so a view never remains
|
|
88
|
-
* logically visible while its outline clips every pixel.
|
|
122
|
+
* Emits one fixed-topology rounded rectangle for both curve families.
|
|
123
|
+
* Circular uses the standard quarter-circle cubic coefficient; continuous
|
|
124
|
+
* keeps both control points at the rectangle corner (coefficient 1), yielding
|
|
125
|
+
* zero endpoint curvature and a visibly smoother shoulder. "Continuous" is a
|
|
126
|
+
* library-defined cross-platform curve, not a claim of pixel identity with
|
|
127
|
+
* Apple's private continuous-corner implementation.
|
|
89
128
|
*/
|
|
90
|
-
internal fun
|
|
91
|
-
|
|
129
|
+
internal fun appendRoundedRectPath(
|
|
130
|
+
path: Path,
|
|
131
|
+
left: Float,
|
|
132
|
+
top: Float,
|
|
133
|
+
right: Float,
|
|
134
|
+
bottom: Float,
|
|
135
|
+
topLeftRadius: Float,
|
|
136
|
+
topRightRadius: Float,
|
|
137
|
+
bottomRightRadius: Float,
|
|
138
|
+
bottomLeftRadius: Float,
|
|
139
|
+
curveCode: Int,
|
|
140
|
+
) {
|
|
141
|
+
val coefficient = if (curveCode == CLIP_CURVE_CONTINUOUS) {
|
|
142
|
+
1f
|
|
143
|
+
} else {
|
|
144
|
+
0.5522848f
|
|
145
|
+
}
|
|
92
146
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
147
|
+
path.moveTo(left + topLeftRadius, top)
|
|
148
|
+
path.lineTo(right - topRightRadius, top)
|
|
149
|
+
path.cubicTo(
|
|
150
|
+
right - topRightRadius + coefficient * topRightRadius,
|
|
151
|
+
top,
|
|
152
|
+
right,
|
|
153
|
+
top + topRightRadius - coefficient * topRightRadius,
|
|
154
|
+
right,
|
|
155
|
+
top + topRightRadius,
|
|
156
|
+
)
|
|
157
|
+
path.lineTo(right, bottom - bottomRightRadius)
|
|
158
|
+
path.cubicTo(
|
|
159
|
+
right,
|
|
160
|
+
bottom - bottomRightRadius + coefficient * bottomRightRadius,
|
|
161
|
+
right - bottomRightRadius + coefficient * bottomRightRadius,
|
|
162
|
+
bottom,
|
|
163
|
+
right - bottomRightRadius,
|
|
164
|
+
bottom,
|
|
165
|
+
)
|
|
166
|
+
path.lineTo(left + bottomLeftRadius, bottom)
|
|
167
|
+
path.cubicTo(
|
|
168
|
+
left + bottomLeftRadius - coefficient * bottomLeftRadius,
|
|
169
|
+
bottom,
|
|
170
|
+
left,
|
|
171
|
+
bottom - bottomLeftRadius + coefficient * bottomLeftRadius,
|
|
172
|
+
left,
|
|
173
|
+
bottom - bottomLeftRadius,
|
|
174
|
+
)
|
|
175
|
+
path.lineTo(left, top + topLeftRadius)
|
|
176
|
+
path.cubicTo(
|
|
177
|
+
left,
|
|
178
|
+
top + topLeftRadius - coefficient * topLeftRadius,
|
|
179
|
+
left + topLeftRadius - coefficient * topLeftRadius,
|
|
180
|
+
top,
|
|
181
|
+
left + topLeftRadius,
|
|
182
|
+
top,
|
|
183
|
+
)
|
|
184
|
+
path.close()
|
|
185
|
+
}
|
|
100
186
|
|
|
101
187
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* is emitted to the outline as a float.
|
|
107
|
-
*
|
|
108
|
-
* Sub-pixel motion that rounds to the cached integers must NOT invalidate the
|
|
109
|
-
* outline — it is carried on the view's translation instead (see
|
|
110
|
-
* SmoothClipView.applyClipPlacement), which is both cheaper and smoother than
|
|
111
|
-
* restaging an identical rect.
|
|
188
|
+
* Hit-tests the actual rendered path. Path has no float `contains` API, so we
|
|
189
|
+
* flatten that same instance to sub-pixel line segments for ACTION_DOWN. This
|
|
190
|
+
* is deliberately off the render/animation hot path and avoids maintaining a
|
|
191
|
+
* second analytical curve that can drift from clipping geometry.
|
|
112
192
|
*/
|
|
113
|
-
internal fun
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
193
|
+
internal fun containsPathPoint(path: Path, x: Float, y: Float): Boolean {
|
|
194
|
+
if (!x.isFinite() || !y.isFinite() || path.isEmpty) return false
|
|
195
|
+
val points = path.approximate(0.1f)
|
|
196
|
+
if (points.size < 6) return false
|
|
197
|
+
|
|
198
|
+
var inside = false
|
|
199
|
+
var previousX = points[1]
|
|
200
|
+
var previousY = points[2]
|
|
201
|
+
var index = 3
|
|
202
|
+
while (index + 2 < points.size) {
|
|
203
|
+
val currentX = points[index + 1]
|
|
204
|
+
val currentY = points[index + 2]
|
|
205
|
+
val dx = currentX - previousX
|
|
206
|
+
val dy = currentY - previousY
|
|
207
|
+
val segmentLengthSquared = dx * dx + dy * dy
|
|
208
|
+
if (segmentLengthSquared > 0f) {
|
|
209
|
+
val projection = (((x - previousX) * dx + (y - previousY) * dy) /
|
|
210
|
+
segmentLengthSquared).coerceIn(0f, 1f)
|
|
211
|
+
val projectedX = previousX + projection * dx
|
|
212
|
+
val projectedY = previousY + projection * dy
|
|
213
|
+
val distanceX = x - projectedX
|
|
214
|
+
val distanceY = y - projectedY
|
|
215
|
+
if (distanceX * distanceX + distanceY * distanceY <= 0.01f) {
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if ((currentY > y) != (previousY > y)) {
|
|
220
|
+
val crossingX = (previousX - currentX) * (y - currentY) /
|
|
221
|
+
(previousY - currentY) + currentX
|
|
222
|
+
if (x < crossingX) inside = !inside
|
|
223
|
+
}
|
|
224
|
+
previousX = currentX
|
|
225
|
+
previousY = currentY
|
|
226
|
+
index += 3
|
|
227
|
+
}
|
|
228
|
+
return inside
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
internal fun renderVisibility(rendersIntoHost: Boolean): Int =
|
|
232
|
+
if (rendersIntoHost) View.VISIBLE else View.INVISIBLE
|
|
130
233
|
|
|
131
234
|
internal fun clipAccessibility(
|
|
132
235
|
isEmpty: Boolean,
|
|
@@ -137,32 +240,3 @@ internal fun clipAccessibility(
|
|
|
137
240
|
} else {
|
|
138
241
|
requestedAccessibility
|
|
139
242
|
}
|
|
140
|
-
|
|
141
|
-
internal fun containsRoundedPointPx(
|
|
142
|
-
x: Float,
|
|
143
|
-
y: Float,
|
|
144
|
-
left: Float,
|
|
145
|
-
top: Float,
|
|
146
|
-
right: Float,
|
|
147
|
-
bottom: Float,
|
|
148
|
-
radius: Float,
|
|
149
|
-
isEmpty: Boolean,
|
|
150
|
-
): Boolean {
|
|
151
|
-
if (isEmpty || x < left || x > right || y < top || y > bottom) {
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (radius <= 0f) return true
|
|
156
|
-
|
|
157
|
-
val innerLeft = left + radius
|
|
158
|
-
val innerRight = right - radius
|
|
159
|
-
val innerTop = top + radius
|
|
160
|
-
val innerBottom = bottom - radius
|
|
161
|
-
if (x in innerLeft..innerRight || y in innerTop..innerBottom) return true
|
|
162
|
-
|
|
163
|
-
val centerX = if (x < innerLeft) innerLeft else innerRight
|
|
164
|
-
val centerY = if (y < innerTop) innerTop else innerBottom
|
|
165
|
-
val dx = x - centerX
|
|
166
|
-
val dy = y - centerY
|
|
167
|
-
return dx * dx + dy * dy <= radius * radius
|
|
168
|
-
}
|
|
@@ -26,9 +26,23 @@ internal object SmoothClipBindings {
|
|
|
26
26
|
y: Double,
|
|
27
27
|
width: Double,
|
|
28
28
|
height: Double,
|
|
29
|
-
|
|
29
|
+
topLeftRadius: Double,
|
|
30
|
+
topRightRadius: Double,
|
|
31
|
+
bottomRightRadius: Double,
|
|
32
|
+
bottomLeftRadius: Double,
|
|
33
|
+
curveCode: Int,
|
|
30
34
|
contentTranslateX: Double,
|
|
31
35
|
contentTranslateY: Double,
|
|
36
|
+
contentScale: Double,
|
|
37
|
+
shadowEnabled: Boolean,
|
|
38
|
+
shadowRed: Double,
|
|
39
|
+
shadowGreen: Double,
|
|
40
|
+
shadowBlue: Double,
|
|
41
|
+
shadowAlpha: Double,
|
|
42
|
+
shadowOffsetX: Double,
|
|
43
|
+
shadowOffsetY: Double,
|
|
44
|
+
shadowBlurRadius: Double,
|
|
45
|
+
shadowSpreadDistance: Double,
|
|
32
46
|
density: Double,
|
|
33
47
|
widthPx: Double,
|
|
34
48
|
heightPx: Double,
|
|
@@ -36,8 +50,9 @@ internal object SmoothClipBindings {
|
|
|
36
50
|
)
|
|
37
51
|
|
|
38
52
|
/**
|
|
39
|
-
* Pushes a registered view's density and pixel host size
|
|
40
|
-
*
|
|
53
|
+
* Pushes a registered view's density and pixel host size. Density converts
|
|
54
|
+
* raw DIP presentations to pixels; host size controls readiness and the
|
|
55
|
+
* fixed rendering viewport, but never rewrites registry geometry.
|
|
41
56
|
*/
|
|
42
57
|
external fun nativeSetViewHostGeometry(
|
|
43
58
|
driverId: Double,
|
|
@@ -49,9 +64,12 @@ internal object SmoothClipBindings {
|
|
|
49
64
|
|
|
50
65
|
external fun nativeUnregisterView(driverId: Double, view: SmoothClipView)
|
|
51
66
|
|
|
67
|
+
/** Destroys a controller after [SmoothClipModule] forwards teardown to main. */
|
|
68
|
+
external fun nativeDestroyDriver(driverId: Double)
|
|
69
|
+
|
|
52
70
|
/**
|
|
53
71
|
* Pushes both positive and negative attachment/window-visibility state.
|
|
54
|
-
* The registry combines it with real host geometry before a
|
|
72
|
+
* The registry combines it with real host geometry before a pending run starts.
|
|
55
73
|
*/
|
|
56
74
|
external fun nativeSetViewLifecycleVisibility(
|
|
57
75
|
driverId: Double,
|
|
@@ -3,6 +3,7 @@ package com.smoothclipview
|
|
|
3
3
|
import com.facebook.react.bridge.Arguments
|
|
4
4
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
5
|
import com.facebook.react.bridge.ReadableArray
|
|
6
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
6
7
|
import com.facebook.react.bridge.WritableArray
|
|
7
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
8
9
|
import com.facebook.react.turbomodule.core.interfaces.BindingsInstallerHolder
|
|
@@ -29,92 +30,112 @@ class SmoothClipModule(context: ReactApplicationContext) :
|
|
|
29
30
|
SmoothClipBindings.nativeInvalidate()
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
// Static complex paths are supported, but autonomous animation remains a
|
|
34
|
+
// measured capability. Keep it disabled until the physical-device path
|
|
35
|
+
// sampling and frame-time gates are run for this release.
|
|
36
|
+
override fun supportsAutonomousComplexPathAnimation(): Boolean = false
|
|
37
|
+
|
|
38
|
+
override fun beginGroupInteraction(driverIds: ReadableArray): WritableArray =
|
|
39
|
+
Arguments.createArray()
|
|
40
|
+
|
|
41
|
+
override fun snapshotGroup(driverIds: ReadableArray): WritableArray =
|
|
42
|
+
Arguments.createArray()
|
|
43
|
+
|
|
44
|
+
override fun setClipPresentationBatch(entries: ReadableArray): Boolean = false
|
|
45
|
+
|
|
46
|
+
override fun animateTimingGroup(
|
|
47
|
+
controllerId: Double,
|
|
48
|
+
entries: ReadableArray,
|
|
49
|
+
durationMs: Double,
|
|
50
|
+
controlPoint1X: Double,
|
|
51
|
+
controlPoint1Y: Double,
|
|
52
|
+
controlPoint2X: Double,
|
|
53
|
+
controlPoint2Y: Double,
|
|
54
|
+
reduceMotion: Double,
|
|
55
|
+
completionTag: Double,
|
|
56
|
+
startTimestamp: Double,
|
|
57
|
+
): Double = 0.0
|
|
58
|
+
|
|
59
|
+
override fun animateSpringGroup(
|
|
60
|
+
controllerId: Double,
|
|
61
|
+
entries: ReadableArray,
|
|
62
|
+
mass: Double,
|
|
63
|
+
stiffness: Double,
|
|
64
|
+
damping: Double,
|
|
65
|
+
velocity: Double,
|
|
66
|
+
energyThreshold: Double,
|
|
67
|
+
reduceMotion: Double,
|
|
68
|
+
completionTag: Double,
|
|
69
|
+
startTimestamp: Double,
|
|
70
|
+
): Double = 0.0
|
|
71
|
+
|
|
72
|
+
override fun cancelAnimationGroup(
|
|
73
|
+
groupId: Double,
|
|
74
|
+
behavior: Double,
|
|
75
|
+
): WritableArray = Arguments.createArray()
|
|
76
|
+
|
|
32
77
|
override fun setClipPresentation(
|
|
78
|
+
driverId: Double,
|
|
79
|
+
presentation: ReadableArray,
|
|
80
|
+
takeOwnership: Boolean,
|
|
81
|
+
overridePendingAnimation: Boolean,
|
|
82
|
+
) = Unit
|
|
83
|
+
|
|
84
|
+
override fun setClipFrameScalars(
|
|
33
85
|
driverId: Double,
|
|
34
86
|
x: Double,
|
|
35
87
|
y: Double,
|
|
36
88
|
width: Double,
|
|
37
89
|
height: Double,
|
|
38
|
-
|
|
90
|
+
topLeftRadius: Double,
|
|
91
|
+
topRightRadius: Double,
|
|
92
|
+
bottomRightRadius: Double,
|
|
93
|
+
bottomLeftRadius: Double,
|
|
94
|
+
curveCode: Double,
|
|
39
95
|
contentTranslateX: Double,
|
|
40
96
|
contentTranslateY: Double,
|
|
41
|
-
|
|
42
|
-
|
|
97
|
+
contentScale: Double,
|
|
98
|
+
shadowEnabled: Boolean,
|
|
99
|
+
shadowRed: Double,
|
|
100
|
+
shadowGreen: Double,
|
|
101
|
+
shadowBlue: Double,
|
|
102
|
+
shadowAlpha: Double,
|
|
103
|
+
shadowOffsetX: Double,
|
|
104
|
+
shadowOffsetY: Double,
|
|
105
|
+
shadowBlurRadius: Double,
|
|
106
|
+
shadowSpreadDistance: Double,
|
|
43
107
|
) = Unit
|
|
44
108
|
|
|
45
109
|
override fun beginInteraction(driverId: Double): WritableArray =
|
|
46
110
|
Arguments.createArray()
|
|
47
111
|
|
|
112
|
+
override fun snapshotCurrent(driverId: Double): WritableArray =
|
|
113
|
+
Arguments.createArray()
|
|
114
|
+
|
|
48
115
|
override fun animateTiming(
|
|
49
116
|
driverId: Double,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
startY: Double,
|
|
53
|
-
startWidth: Double,
|
|
54
|
-
startHeight: Double,
|
|
55
|
-
startRadius: Double,
|
|
56
|
-
startContentTranslateX: Double,
|
|
57
|
-
startContentTranslateY: Double,
|
|
58
|
-
x: Double,
|
|
59
|
-
y: Double,
|
|
60
|
-
width: Double,
|
|
61
|
-
height: Double,
|
|
62
|
-
radius: Double,
|
|
63
|
-
contentTranslateX: Double,
|
|
64
|
-
contentTranslateY: Double,
|
|
117
|
+
start: ReadableArray,
|
|
118
|
+
target: ReadableArray,
|
|
65
119
|
durationMs: Double,
|
|
66
120
|
controlPoint1X: Double,
|
|
67
121
|
controlPoint1Y: Double,
|
|
68
122
|
controlPoint2X: Double,
|
|
69
123
|
controlPoint2Y: Double,
|
|
70
124
|
reduceMotion: Double,
|
|
125
|
+
completionTag: Double,
|
|
71
126
|
): Double = 0.0
|
|
72
127
|
|
|
73
128
|
override fun animateSpring(
|
|
74
129
|
driverId: Double,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
startY: Double,
|
|
78
|
-
startWidth: Double,
|
|
79
|
-
startHeight: Double,
|
|
80
|
-
startRadius: Double,
|
|
81
|
-
startContentTranslateX: Double,
|
|
82
|
-
startContentTranslateY: Double,
|
|
83
|
-
x: Double,
|
|
84
|
-
y: Double,
|
|
85
|
-
width: Double,
|
|
86
|
-
height: Double,
|
|
87
|
-
radius: Double,
|
|
88
|
-
contentTranslateX: Double,
|
|
89
|
-
contentTranslateY: Double,
|
|
130
|
+
start: ReadableArray,
|
|
131
|
+
target: ReadableArray,
|
|
90
132
|
mass: Double,
|
|
91
133
|
stiffness: Double,
|
|
92
134
|
damping: Double,
|
|
93
135
|
initialVelocity: Double,
|
|
94
136
|
inheritVelocity: Boolean,
|
|
95
137
|
reduceMotion: Double,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
override fun animateKeyframes(
|
|
99
|
-
driverId: Double,
|
|
100
|
-
hasInteractiveStart: Boolean,
|
|
101
|
-
startX: Double,
|
|
102
|
-
startY: Double,
|
|
103
|
-
startWidth: Double,
|
|
104
|
-
startHeight: Double,
|
|
105
|
-
startRadius: Double,
|
|
106
|
-
startContentTranslateX: Double,
|
|
107
|
-
startContentTranslateY: Double,
|
|
108
|
-
x: Double,
|
|
109
|
-
y: Double,
|
|
110
|
-
width: Double,
|
|
111
|
-
height: Double,
|
|
112
|
-
radius: Double,
|
|
113
|
-
contentTranslateX: Double,
|
|
114
|
-
contentTranslateY: Double,
|
|
115
|
-
durationMs: Double,
|
|
116
|
-
frames: ReadableArray,
|
|
117
|
-
reduceMotion: Double,
|
|
138
|
+
completionTag: Double,
|
|
118
139
|
): Double = 0.0
|
|
119
140
|
|
|
120
141
|
override fun rejectAnimation(driverId: Double): Double = 0.0
|
|
@@ -125,5 +146,9 @@ class SmoothClipModule(context: ReactApplicationContext) :
|
|
|
125
146
|
behavior: Double,
|
|
126
147
|
): WritableArray = Arguments.createArray()
|
|
127
148
|
|
|
128
|
-
override fun destroyDriver(driverId: Double)
|
|
149
|
+
override fun destroyDriver(driverId: Double) {
|
|
150
|
+
UiThreadUtil.runOnUiThread {
|
|
151
|
+
SmoothClipBindings.nativeDestroyDriver(driverId)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
129
154
|
}
|