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.
Files changed (130) hide show
  1. package/README.md +60 -21
  2. package/android/build.gradle +6 -2
  3. package/android/proguard-rules.pro +3 -3
  4. package/android/src/main/cpp/SmoothClipAndroid.h +10 -0
  5. package/android/src/main/cpp/SmoothClipBindings.cpp +890 -4
  6. package/android/src/main/cpp/SmoothClipRegistry.cpp +1056 -44
  7. package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +265 -0
  8. package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +21 -0
  9. package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +283 -0
  10. package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +371 -18
  11. package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +138 -24
  12. package/cpp/SmoothClipAnimationCurve.h +151 -122
  13. package/cpp/SmoothClipRegistry.h +95 -3
  14. package/cpp/SmoothClipSharedGeometry.h +146 -7
  15. package/cpp/SmoothClipVelocityTracker.h +13 -9
  16. package/ios/SmoothClipGeometry.h +184 -6
  17. package/ios/SmoothClipRegistry.mm +1028 -22
  18. package/ios/SmoothClipRegistrySnapshot.h +13 -0
  19. package/ios/SmoothClipTurboModule.cpp +857 -2
  20. package/ios/SmoothClipTurboModule.h +310 -0
  21. package/ios/SmoothClipView.mm +714 -183
  22. package/ios/SmoothClipViewRegistry.h +1 -0
  23. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  24. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  25. package/lib/module/NativeSmoothClipModule.js.map +1 -1
  26. package/lib/module/SmoothClipView.ios.js +22 -6
  27. package/lib/module/SmoothClipView.ios.js.map +1 -1
  28. package/lib/module/SmoothClipView.js +1 -46
  29. package/lib/module/SmoothClipView.js.map +1 -1
  30. package/lib/module/SmoothClipViewNativeComponent.ts +27 -1
  31. package/lib/module/capabilities.js +4 -0
  32. package/lib/module/capabilities.js.map +1 -0
  33. package/lib/module/capabilities.native.js +36 -0
  34. package/lib/module/capabilities.native.js.map +1 -0
  35. package/lib/module/capabilityTypes.js +11 -0
  36. package/lib/module/capabilityTypes.js.map +1 -0
  37. package/lib/module/driverState.js +0 -28
  38. package/lib/module/driverState.js.map +1 -1
  39. package/lib/module/drivers.js +1 -281
  40. package/lib/module/drivers.js.map +1 -1
  41. package/lib/module/drivers.native.js +263 -63
  42. package/lib/module/drivers.native.js.map +1 -1
  43. package/lib/module/geometry.js +147 -17
  44. package/lib/module/geometry.js.map +1 -1
  45. package/lib/module/groupDriverTypes.js +4 -0
  46. package/lib/module/groupDriverTypes.js.map +1 -0
  47. package/lib/module/groupDrivers.android.js +4 -0
  48. package/lib/module/groupDrivers.android.js.map +1 -0
  49. package/lib/module/groupDrivers.ios.js +4 -0
  50. package/lib/module/groupDrivers.ios.js.map +1 -0
  51. package/lib/module/groupDrivers.js +4 -0
  52. package/lib/module/groupDrivers.js.map +1 -0
  53. package/lib/module/groupDrivers.native.js +799 -0
  54. package/lib/module/groupDrivers.native.js.map +1 -0
  55. package/lib/module/index.js +3 -2
  56. package/lib/module/index.js.map +1 -1
  57. package/lib/module/presentationProtocol.js +15 -0
  58. package/lib/module/presentationProtocol.js.map +1 -0
  59. package/lib/typescript/src/NativeSmoothClipModule.d.ts +25 -0
  60. package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
  61. package/lib/typescript/src/SmoothClipView.d.ts +2 -10
  62. package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
  63. package/lib/typescript/src/SmoothClipView.ios.d.ts +133 -2
  64. package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
  65. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +8 -0
  66. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
  67. package/lib/typescript/src/capabilities.d.ts +3 -0
  68. package/lib/typescript/src/capabilities.d.ts.map +1 -0
  69. package/lib/typescript/src/capabilities.native.d.ts +3 -0
  70. package/lib/typescript/src/capabilities.native.d.ts.map +1 -0
  71. package/lib/typescript/src/capabilityTypes.d.ts +10 -0
  72. package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
  73. package/lib/typescript/src/driverState.d.ts +1 -6
  74. package/lib/typescript/src/driverState.d.ts.map +1 -1
  75. package/lib/typescript/src/driverTypes.d.ts +25 -11
  76. package/lib/typescript/src/driverTypes.d.ts.map +1 -1
  77. package/lib/typescript/src/drivers.d.ts +2 -4
  78. package/lib/typescript/src/drivers.d.ts.map +1 -1
  79. package/lib/typescript/src/drivers.native.d.ts.map +1 -1
  80. package/lib/typescript/src/geometry.d.ts +36 -2
  81. package/lib/typescript/src/geometry.d.ts.map +1 -1
  82. package/lib/typescript/src/groupDriverTypes.d.ts +66 -0
  83. package/lib/typescript/src/groupDriverTypes.d.ts.map +1 -0
  84. package/lib/typescript/src/groupDrivers.android.d.ts +2 -0
  85. package/lib/typescript/src/groupDrivers.android.d.ts.map +1 -0
  86. package/lib/typescript/src/groupDrivers.d.ts +2 -0
  87. package/lib/typescript/src/groupDrivers.d.ts.map +1 -0
  88. package/lib/typescript/src/groupDrivers.ios.d.ts +2 -0
  89. package/lib/typescript/src/groupDrivers.ios.d.ts.map +1 -0
  90. package/lib/typescript/src/groupDrivers.native.d.ts +4 -0
  91. package/lib/typescript/src/groupDrivers.native.d.ts.map +1 -0
  92. package/lib/typescript/src/index.d.ts +6 -3
  93. package/lib/typescript/src/index.d.ts.map +1 -1
  94. package/lib/typescript/src/presentationProtocol.d.ts +4 -0
  95. package/lib/typescript/src/presentationProtocol.d.ts.map +1 -0
  96. package/package.json +10 -11
  97. package/src/NativeSmoothClipModule.ts +275 -0
  98. package/src/SmoothClipView.ios.tsx +30 -11
  99. package/src/SmoothClipView.tsx +2 -76
  100. package/src/SmoothClipViewNativeComponent.ts +27 -1
  101. package/src/capabilities.native.ts +69 -0
  102. package/src/capabilities.ts +2 -0
  103. package/src/capabilityTypes.ts +17 -0
  104. package/src/driverState.ts +1 -41
  105. package/src/driverTypes.ts +42 -11
  106. package/src/drivers.native.ts +824 -124
  107. package/src/drivers.ts +2 -423
  108. package/src/geometry.ts +231 -27
  109. package/src/groupDriverTypes.ts +117 -0
  110. package/src/groupDrivers.android.ts +1 -0
  111. package/src/groupDrivers.ios.ts +1 -0
  112. package/src/groupDrivers.native.ts +1189 -0
  113. package/src/groupDrivers.ts +1 -0
  114. package/src/index.ts +30 -2
  115. package/src/presentationProtocol.ts +28 -0
  116. package/lib/module/SmoothClipView.web.js +0 -54
  117. package/lib/module/SmoothClipView.web.js.map +0 -1
  118. package/lib/module/index.web.js +0 -8
  119. package/lib/module/index.web.js.map +0 -1
  120. package/lib/module/webClipPath.js +0 -25
  121. package/lib/module/webClipPath.js.map +0 -1
  122. package/lib/typescript/src/SmoothClipView.web.d.ts +0 -10
  123. package/lib/typescript/src/SmoothClipView.web.d.ts.map +0 -1
  124. package/lib/typescript/src/index.web.d.ts +0 -9
  125. package/lib/typescript/src/index.web.d.ts.map +0 -1
  126. package/lib/typescript/src/webClipPath.d.ts +0 -7
  127. package/lib/typescript/src/webClipPath.d.ts.map +0 -1
  128. package/src/SmoothClipView.web.tsx +0 -66
  129. package/src/index.web.ts +0 -23
  130. package/src/webClipPath.ts +0 -30
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/react-native-smooth-clip-view.svg)](https://www.npmjs.com/package/react-native-smooth-clip-view)
4
4
 
5
+ https://github.com/user-attachments/assets/899235b3-de69-46d7-b6db-61bc54d80df8
6
+
7
+ <sub>Card zoom and shared-element gallery transitions, captured at 60fps — [direct clip](https://github.com/kbrattli/react-native-smooth-clip-view/blob/main/docs/media/smooth-clip-demo.mp4).</sub>
8
+
5
9
  ## High-performance geometry animations for React Native
6
10
 
7
11
  `react-native-smooth-clip-view` lets you animate `x`, `y`, `width`, `height`, and `borderRadius` with Reanimated without triggering expensive layout work on every frame.
@@ -16,10 +20,11 @@ Use it for transitions that previously struggled with performance when animating
16
20
  - React Native Reanimated 4.5 or newer
17
21
  - React Native Worklets 0.10 or newer
18
22
  - iOS 16.4 or newer
19
- - Android API 26 or newer
23
+ - Android API 33 or newer
20
24
 
21
- The package supports iOS, Android, and React Native Web. It does not include a
22
- legacy Paper implementation.
25
+ The package supports iOS and Android. React Native Web and the legacy Paper
26
+ architecture are not supported; keep imports and rendered usage behind native
27
+ platform boundaries.
23
28
 
24
29
  ## Installation
25
30
 
@@ -97,8 +102,9 @@ const styles = StyleSheet.create({
97
102
  ```
98
103
 
99
104
  The same driver can be passed to multiple hosts. One native registry update
100
- fans the seven-scalar presentation out to all mounted hosts. Interactive
101
- updates avoid Yoga and ShadowTree commits.
105
+ fans the presentation out to all mounted hosts. The original seven-scalar V1
106
+ protocol remains unchanged; V2 adds per-corner radii, continuous curves, and
107
+ centered content scale. Interactive updates avoid Yoga and ShadowTree commits.
102
108
 
103
109
  For transitions whose endpoint is known, let Core Animation interpolate on the
104
110
  render server:
@@ -211,13 +217,18 @@ puts on screen, which is the property worth keeping identical.
211
217
  as `animation.from` — `animateTo` then performs the hot write and the
212
218
  handoff in one call. Do not interleave `setScalars` with
213
219
  `presentation.value` writes on the same driver.
220
+ - `driver.ui.setPresentationScalars(x, y, width, height, topLeft, topRight,
221
+ bottomRight, bottomLeft, curveCode, tx, ty, scale)` is the V2 hot path.
222
+ `curveCode` is `0` for circular and `1` for continuous. V2 values are
223
+ validated as one transaction; widened values are never silently downgraded
224
+ onto a V1 native binary.
214
225
  - Spring `initialVelocity` is one normalized scalar along the current-to-target
215
226
  trajectory, in units of the remaining distance per second (`1` covers the
216
227
  remaining distance in one second). Every geometry channel continues with the
217
228
  same normalized rate, so grab/release preserves the felt direction and
218
229
  speed. `'inherit'` (the default) estimates the scalar from the last two
219
- interactive samples on iOS and Android; the web fallback always inherits
220
- zero. On Android, sampling on the `setScalars` hot path is **opt-in**: pass
230
+ interactive samples on iOS and Android. On Android, sampling on the
231
+ `setScalars` hot path is **opt-in**: pass
221
232
  `velocityTracking: true` to `useSmoothClipDriver` or `'inherit'` inherits
222
233
  zero after a hot-write drag (a dev-mode warning flags this). **Behavior
223
234
  change:** earlier releases always recorded on Android, so an existing
@@ -261,10 +272,9 @@ puts on screen, which is the property worth keeping identical.
261
272
  starts the replacement from that native value. Passive hook seeds and public
262
273
  `set`/`setScalars` writes still leave a held latch intact. `from` behaves the
263
274
  same on both platforms (it is driver-layer, not native): on iOS the seed
264
- stops any running Core Animation and writes the model layer first; keyframes
265
- then start exactly at `from`, while timing/spring sample their from-value off
266
- the presentation layer — the last committed frame, at most one frame behind
267
- `from` — identical to the explicit two-call pattern.
275
+ stops any running Core Animation, applies `from` to the model layer, and
276
+ installs timing, spring, or keyframe playback from that exact presentation in
277
+ the same start transaction.
268
278
  - `cancel()` freezes visible presentation by default. Pass `'target'` as its
269
279
  behavior to jump to the requested endpoint.
270
280
  - `options.onAnimationComplete` fires exactly once per animation with its ID
@@ -326,6 +336,30 @@ bounds, so a clip that extended beyond the host comes back clamped. Start
326
336
  gestures with `beginInteraction()`: interactive writes issued while native
327
337
  still owns rendering are dropped.
328
338
 
339
+ ### Group driver
340
+
341
+ `useSmoothClipGroupDriver({ reduceMotion, onAnimationComplete })` coordinates
342
+ multiple drivers with one native group ID and one completion. Its worklet-safe
343
+ `ui` and Promise-based `react` interfaces expose:
344
+
345
+ - `beginInteraction(drivers)` to freeze every overlapping group atomically and
346
+ return canonical visible snapshots in input order.
347
+ - `snapshotCurrent(drivers)` to sample presentation and readiness without
348
+ changing ownership.
349
+ - `setBatch(entries)` to validate every entry before committing one native
350
+ transaction.
351
+ - `animateTo(entries, animation)` for shared timing, spring, or linear
352
+ keyframe progress. Membership is immutable for that group ID; a replacement
353
+ is a new atomically installed group.
354
+ - `cancel(groupId, 'freeze' | 'finish')` to return every participant snapshot.
355
+
356
+ Groups wait until all participants have a visible, attached, laid-out,
357
+ positive-size host. The default `suspensionPolicy: 'pause'` freezes and
358
+ re-latches the whole group if any participant loses readiness; `'finish'`
359
+ applies every target. Complex-path native settlement is capability-gated—use
360
+ `getSmoothClipCapabilities()` and keep streaming with `setBatch` when
361
+ `autonomousComplexPathAnimation` is false.
362
+
329
363
  ### `SmoothClipPresentation`
330
364
 
331
365
  ```ts
@@ -333,6 +367,7 @@ type SmoothClipPresentation = Readonly<{
333
367
  clip: ClipGeometry;
334
368
  contentTranslateX: number;
335
369
  contentTranslateY: number;
370
+ contentScale?: number;
336
371
  }>;
337
372
  ```
338
373
 
@@ -349,12 +384,19 @@ type ClipGeometry = Readonly<{
349
384
  width: number;
350
385
  height: number;
351
386
  radius: number;
387
+ topLeftRadius?: number;
388
+ topRightRadius?: number;
389
+ bottomRightRadius?: number;
390
+ bottomLeftRadius?: number;
391
+ curve?: 'circular' | 'continuous';
352
392
  }>;
353
393
  ```
354
394
 
355
395
  Values use React Native points/DIPs. Native code rejects non-finite updates,
356
396
  intersects the requested rectangle with the actual host bounds, prevents
357
- negative sizes, and clamps `radius` to half of the visible shortest edge.
397
+ negative sizes, and proportionally scales overlapping corners with the CSS
398
+ corner-normalization rule. Content scale is centered on the native content
399
+ container; translation is independent and is not multiplied by scale.
358
400
 
359
401
  ### `normalizeClipGeometry`
360
402
 
@@ -376,23 +418,21 @@ pass it as the `driver` prop, and move per-frame updates from the
376
418
  - Give the host its fixed maximum `width` and `height`; clipping never changes
377
419
  Yoga layout.
378
420
  - Put visual backgrounds inside `SmoothClipView`.
379
- - Borders, shadows, host corner styles, host `overflow`, per-corner radii, and
380
- externally applied anisotropic transforms are unsupported.
381
- - Version 0.1.0 supports one uniform corner radius.
382
- - On web, the implementation uses CSS `clip-path` on the fixed host, so
383
- descendant percentages and right/bottom anchoring still resolve against the
384
- maximum footprint.
421
+ - Keep borders, shadows, rotation, and anisotropic transforms on an outer
422
+ visual carrier rather than the clip host.
423
+ - Uniform circular corners use platform fast paths. Unequal or continuous
424
+ corners use a fixed-topology portable path; Android intentionally does not
425
+ claim pixel identity with Apple's proprietary continuous curve.
385
426
 
386
427
  ## Example and development
387
428
 
388
429
  The [`example`](./example) workspace is an Expo SDK 57 app that exercises the
389
- package through its public import on iOS, Android, and web.
430
+ package through its public import on iOS and Android.
390
431
 
391
432
  ```sh
392
433
  npm install
393
434
  npm run example -- ios
394
435
  npm run example -- android
395
- npm run example -- web
396
436
  ```
397
437
 
398
438
  Run the repository checks with:
@@ -402,7 +442,6 @@ npm run lint
402
442
  npm run typecheck
403
443
  npm test
404
444
  npm run prepare
405
- npm run example -- build:web
406
445
  ```
407
446
 
408
447
  ## License
@@ -1,7 +1,7 @@
1
1
  buildscript {
2
2
  ext.SmoothClipView = [
3
3
  kotlinVersion: "2.0.21",
4
- minSdkVersion: 26,
4
+ minSdkVersion: 33,
5
5
  compileSdkVersion: 36,
6
6
  targetSdkVersion: 36
7
7
  ]
@@ -76,7 +76,11 @@ android {
76
76
  }
77
77
 
78
78
  defaultConfig {
79
- minSdkVersion getExtOrDefault("minSdkVersion")
79
+ // Outline.setPath only guarantees arbitrary path clipping from API 33.
80
+ // Keep this as a hard library floor: allowing a consuming root project to
81
+ // override it lower would make the V2 per-corner/continuous path silently
82
+ // unsupported at runtime.
83
+ minSdkVersion SmoothClipView.minSdkVersion
80
84
  targetSdkVersion getExtOrDefault("targetSdkVersion")
81
85
  // C++ resolves SmoothClipView by name and calls setClipPresentationDip via
82
86
  // a cached jmethodID; consumer minification must not rename or strip them.
@@ -1,6 +1,6 @@
1
1
  # SmoothClipView is resolved from C++ by name (fbjni findClassStatic) and its
2
- # setClipPresentationDip / setClipPresentationPx are invoked through cached
3
- # jmethodIDs; SmoothClipBindings registers native methods by name. None of them
4
- # may be renamed or stripped in consumer release builds.
2
+ # V1/V2 setClipPresentation*Dip / setClipPresentation*Px are invoked through
3
+ # cached jmethodIDs; SmoothClipBindings registers native methods by name. None
4
+ # of them may be renamed or stripped in consumer release builds.
5
5
  -keep class com.smoothclipview.SmoothClipView { *; }
6
6
  -keep class com.smoothclipview.SmoothClipBindings { *; }
@@ -18,10 +18,16 @@ struct JSmoothClipView : facebook::jni::JavaClass<JSmoothClipView> {
18
18
  static constexpr auto kJavaDescriptor = "Lcom/smoothclipview/SmoothClipView;";
19
19
 
20
20
  void applyClip(const Presentation &presentation) const;
21
+ void applyClipV2(const Presentation &presentation) const;
21
22
  void applyClipPx(
22
23
  const NormalizedClip &clip,
23
24
  double contentTranslateXPx,
24
25
  double contentTranslateYPx) const;
26
+ void applyClipV2Px(
27
+ const NormalizedClip &clip,
28
+ double contentTranslateXPx,
29
+ double contentTranslateYPx,
30
+ double contentScale) const;
25
31
  };
26
32
 
27
33
  // Called from Kotlin (SmoothClipViewManager) on the UI thread when a Fabric
@@ -57,6 +63,10 @@ void setViewLifecycleVisibilityAndroid(
57
63
  // seconds; never lets a failure unwind back into doFrame.
58
64
  void onFrameAndroid(double frameTimeS);
59
65
 
66
+ // Non-destructive V2 snapshot for the public snapshotCurrentV2 binding.
67
+ // beginInteraction remains the explicit ownership-taking/cancelling operation.
68
+ Presentation snapshotCurrentAndroid(uint64_t driverId);
69
+
60
70
  // Installs `global.__SmoothClipView` (worklet-callable host functions) into the
61
71
  // JS runtime and wires native animation completion delivery through the
62
72
  // CallInvoker. Invoked by the TurboModule BindingsInstaller.