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/src/drivers.ts CHANGED
@@ -1,427 +1,6 @@
1
- import { useEffect, useRef } from 'react';
2
- import {
3
- cancelAnimation as cancelReanimatedAnimation,
4
- Easing,
5
- ReduceMotion,
6
- useAnimatedReaction,
7
- useSharedValue,
8
- withSpring,
9
- withTiming,
10
- } from 'react-native-reanimated';
11
- import { scheduleOnRN, scheduleOnUI } from 'react-native-worklets';
12
- import type {
13
- ClipReduceMotion,
14
- KeyframedClipAnimation,
15
- SmoothClipAnimation,
16
- SmoothClipDriver,
17
- SmoothClipDriverOptions,
18
- SpringClipAnimation,
19
- TimingClipAnimation,
20
- } from './driverTypes';
21
- import {
22
- allocateDriverId,
23
- attachDriverState,
24
- createDriverState,
25
- deliverDriverCompletion,
26
- detachDriverState,
27
- finishDriverAnimation,
28
- getDriverState,
29
- setDriverState,
30
- snapshotDriverViews,
31
- } from './driverState';
32
- import {
33
- createClipPresentation,
34
- clipPresentationEquals,
35
- isFiniteClipPresentation,
36
- type ClipGeometry,
37
- type SmoothClipPresentation,
38
- } from './geometry';
39
- import {
40
- createReactRequest,
41
- rejectDriverRequests,
42
- resolveReactRequest,
43
- } from './reactRequests';
44
-
45
- function toReanimatedReduceMotion(value: ClipReduceMotion): ReduceMotion {
46
- switch (value) {
47
- case 'always':
48
- return ReduceMotion.Always;
49
- case 'never':
50
- return ReduceMotion.Never;
51
- default:
52
- return ReduceMotion.System;
53
- }
54
- }
55
-
56
- function animationIsFinite(animation: SmoothClipAnimation): boolean {
57
- 'worklet';
58
- if (
59
- animation.from !== undefined &&
60
- !isFiniteClipPresentation(animation.from)
61
- ) {
62
- return false;
63
- }
64
- if (animation.type === 'timing') {
65
- return (
66
- Number.isFinite(animation.duration) &&
67
- animation.controlPoints.every(Number.isFinite)
68
- );
69
- }
70
- if (animation.type === 'spring') {
71
- return [
72
- animation.mass ?? 1,
73
- animation.stiffness ?? 100,
74
- animation.damping ?? 10,
75
- animation.initialVelocity === 'inherit'
76
- ? 0
77
- : (animation.initialVelocity ?? 0),
78
- ].every(Number.isFinite);
79
- }
80
- let previousOffset = -1;
81
- if (!Number.isFinite(animation.duration) || animation.frames.length < 2) {
82
- return false;
83
- }
84
- for (const frame of animation.frames) {
85
- if (
86
- !Number.isFinite(frame.offset) ||
87
- frame.offset < 0 ||
88
- frame.offset > 1 ||
89
- frame.offset <= previousOffset ||
90
- !isFiniteClipPresentation(frame.presentation)
91
- ) {
92
- return false;
93
- }
94
- previousOffset = frame.offset;
95
- }
96
- return animation.frames[0]?.offset === 0 && previousOffset === 1;
97
- }
98
-
99
- function interpolatePresentation(
100
- from: SmoothClipPresentation,
101
- to: SmoothClipPresentation,
102
- progress: number
103
- ): SmoothClipPresentation {
104
- 'worklet';
105
- const mix = (start: number, end: number) => start + (end - start) * progress;
106
- return {
107
- clip: {
108
- x: mix(from.clip.x, to.clip.x),
109
- y: mix(from.clip.y, to.clip.y),
110
- width: mix(from.clip.width, to.clip.width),
111
- height: mix(from.clip.height, to.clip.height),
112
- radius: mix(from.clip.radius, to.clip.radius),
113
- },
114
- contentTranslateX: mix(from.contentTranslateX, to.contentTranslateX),
115
- contentTranslateY: mix(from.contentTranslateY, to.contentTranslateY),
116
- };
117
- }
118
-
119
- function finishFallback(
120
- driverId: number,
121
- animationId: number,
122
- finished: boolean
123
- ): void {
124
- deliverDriverCompletion(
125
- driverId,
126
- animationId,
127
- finishDriverAnimation(driverId, animationId, finished)
128
- );
129
- }
130
-
131
- export function useSmoothClipDriver(
132
- initialValue: ClipGeometry | SmoothClipPresentation,
133
- options: SmoothClipDriverOptions = {}
134
- ): SmoothClipDriver {
135
- const initialPresentation =
136
- 'clip' in initialValue
137
- ? initialValue
138
- : createClipPresentation(initialValue);
139
- const presentation = useSharedValue(initialPresentation);
140
- const activeAnimationId = useSharedValue(0);
141
- const nextAnimationId = useSharedValue(0);
142
- const latestTarget = useSharedValue(initialPresentation);
143
- const animationStart = useSharedValue(initialPresentation);
144
- const animationProgress = useSharedValue(0);
145
- const keyframes = useSharedValue<
146
- readonly Readonly<{
147
- offset: number;
148
- presentation: SmoothClipPresentation;
149
- }>[]
150
- >([]);
151
- const callbackRef = useRef(options.onAnimationComplete);
152
- const driverRef = useRef<SmoothClipDriver | null>(null);
153
- callbackRef.current = options.onAnimationComplete;
154
-
155
- useAnimatedReaction(
156
- () => animationProgress.value,
157
- (progress) => {
158
- const frames = keyframes.value;
159
- if (frames.length >= 2) {
160
- let upperIndex = 1;
161
- while (
162
- upperIndex < frames.length - 1 &&
163
- progress > (frames[upperIndex]?.offset ?? 1)
164
- ) {
165
- upperIndex += 1;
166
- }
167
- const lower = frames[upperIndex - 1];
168
- const upper = frames[upperIndex];
169
- if (!lower || !upper) return;
170
- const span = upper.offset - lower.offset;
171
- const localProgress = span <= 0 ? 1 : (progress - lower.offset) / span;
172
- presentation.value = interpolatePresentation(
173
- lower.presentation,
174
- upper.presentation,
175
- Math.min(1, Math.max(0, localProgress))
176
- );
177
- return;
178
- }
179
- presentation.value = interpolatePresentation(
180
- animationStart.value,
181
- latestTarget.value,
182
- progress
183
- );
184
- },
185
- [animationProgress, animationStart, keyframes, latestTarget, presentation]
186
- );
187
-
188
- if (driverRef.current === null) {
189
- const driverId = allocateDriverId();
190
- const reduceMotion = toReanimatedReduceMotion(
191
- options.reduceMotion ?? 'system'
192
- );
193
-
194
- const beginOnUI = (): SmoothClipPresentation => {
195
- 'worklet';
196
- cancelReanimatedAnimation(animationProgress);
197
- activeAnimationId.value = 0;
198
- return presentation.value;
199
- };
200
-
201
- const setOnUI = (next: SmoothClipPresentation): void => {
202
- 'worklet';
203
- if (!isFiniteClipPresentation(next)) return;
204
- cancelReanimatedAnimation(animationProgress);
205
- activeAnimationId.value = 0;
206
- latestTarget.value = next;
207
- presentation.value = next;
208
- };
209
-
210
- const animateOnUI = (
211
- next: SmoothClipPresentation,
212
- animation: SmoothClipAnimation
213
- ): number => {
214
- 'worklet';
215
- nextAnimationId.value = (nextAnimationId.value % 0x7ffffffe) + 1;
216
- const animationId = nextAnimationId.value;
217
- if (!isFiniteClipPresentation(next) || !animationIsFinite(animation)) {
218
- scheduleOnRN(finishFallback, driverId, animationId, false);
219
- return animationId;
220
- }
221
-
222
- if (animation.from !== undefined) {
223
- // Fused explicit start: on the fallback driver the setScalars hot
224
- // path is a plain presentation write, so the desugar is the same.
225
- presentation.value = animation.from;
226
- }
227
- activeAnimationId.value = animationId;
228
- animationStart.value = presentation.value;
229
- latestTarget.value = next;
230
- keyframes.value = [];
231
- animationProgress.value = 0;
232
- scheduleOnRN(snapshotDriverViews, driverId, animationId);
233
- const onComplete = (finished?: boolean) => {
234
- 'worklet';
235
- if (activeAnimationId.value === animationId) {
236
- activeAnimationId.value = 0;
237
- }
238
- scheduleOnRN(finishFallback, driverId, animationId, finished === true);
239
- };
240
-
241
- if (animation.type === 'timing') {
242
- const [x1, y1, x2, y2] = animation.controlPoints;
243
- animationProgress.value = withTiming(
244
- 1,
245
- {
246
- duration: Math.max(0, animation.duration),
247
- easing: Easing.bezier(x1, y1, x2, y2),
248
- reduceMotion,
249
- },
250
- onComplete
251
- );
252
- } else if (animation.type === 'spring') {
253
- const configuredVelocity = animation.initialVelocity;
254
- animationProgress.value = withSpring(
255
- 1,
256
- {
257
- damping: animation.damping ?? 10,
258
- mass: animation.mass ?? 1,
259
- reduceMotion,
260
- stiffness: animation.stiffness ?? 100,
261
- // The fallback driver cannot sample native presentation motion,
262
- // so 'inherit' (and undefined) degrade to zero launch velocity.
263
- velocity:
264
- typeof configuredVelocity === 'number' ? configuredVelocity : 0,
265
- },
266
- onComplete
267
- );
268
- } else {
269
- if (
270
- !clipPresentationEquals(
271
- animation.frames[animation.frames.length - 1]?.presentation ?? null,
272
- next
273
- )
274
- ) {
275
- scheduleOnRN(finishFallback, driverId, animationId, false);
276
- activeAnimationId.value = 0;
277
- return animationId;
278
- }
279
- keyframes.value = animation.frames;
280
- animationProgress.value = withTiming(
281
- 1,
282
- {
283
- duration: Math.max(0, animation.duration),
284
- easing: Easing.linear,
285
- reduceMotion,
286
- },
287
- onComplete
288
- );
289
- }
290
- return animationId;
291
- };
292
-
293
- const cancelOnUI = (
294
- animationId = 0,
295
- behavior: 'current' | 'target' = 'current'
296
- ): SmoothClipPresentation => {
297
- 'worklet';
298
- if (animationId !== 0 && animationId !== activeAnimationId.value) {
299
- return presentation.value;
300
- }
301
- cancelReanimatedAnimation(animationProgress);
302
- activeAnimationId.value = 0;
303
- if (behavior === 'target') presentation.value = latestTarget.value;
304
- return presentation.value;
305
- };
306
-
307
- const setScalarsOnUI = (
308
- x: number,
309
- y: number,
310
- width: number,
311
- height: number,
312
- radius: number,
313
- contentTranslateX: number,
314
- contentTranslateY: number
315
- ): void => {
316
- 'worklet';
317
- setOnUI({
318
- clip: { x, y, width, height, radius },
319
- contentTranslateX,
320
- contentTranslateY,
321
- });
322
- };
323
-
324
- const driver: SmoothClipDriver = {
325
- kind: 'hybrid',
326
- presentation,
327
- // Every worklet here shares the single JS runtime, so unlike the native
328
- // driver there is no runtime guard on `ui`: calling it from React code
329
- // is harmless in this fallback.
330
- ui: {
331
- beginInteraction: beginOnUI,
332
- set: setOnUI,
333
- setScalars: setScalarsOnUI,
334
- animateTo: animateOnUI,
335
- cancel: cancelOnUI,
336
- },
337
- react: {
338
- beginInteraction() {
339
- const { requestId, promise } =
340
- createReactRequest<SmoothClipPresentation>(driverId);
341
- scheduleOnUI(() => {
342
- 'worklet';
343
- const result = beginOnUI();
344
- scheduleOnRN(resolveReactRequest, driverId, requestId, result);
345
- });
346
- return promise;
347
- },
348
- set(next) {
349
- const { requestId, promise } = createReactRequest<void>(driverId);
350
- scheduleOnUI(() => {
351
- 'worklet';
352
- setOnUI(next);
353
- scheduleOnRN(resolveReactRequest, driverId, requestId, undefined);
354
- });
355
- return promise;
356
- },
357
- animateTo(next, animation) {
358
- const { requestId, promise } = createReactRequest<number>(
359
- driverId,
360
- true
361
- );
362
- scheduleOnUI(() => {
363
- 'worklet';
364
- const result = animateOnUI(next, animation);
365
- scheduleOnRN(
366
- resolveReactRequest,
367
- driverId,
368
- requestId,
369
- result,
370
- true
371
- );
372
- });
373
- return promise;
374
- },
375
- cancel(animationId = 0, behavior = 'current') {
376
- const { requestId, promise } =
377
- createReactRequest<SmoothClipPresentation>(driverId, true);
378
- scheduleOnUI(() => {
379
- 'worklet';
380
- const result = cancelOnUI(animationId, behavior);
381
- scheduleOnRN(
382
- resolveReactRequest,
383
- driverId,
384
- requestId,
385
- result,
386
- true
387
- );
388
- });
389
- return promise;
390
- },
391
- },
392
- };
393
- setDriverState(
394
- driver,
395
- createDriverState(
396
- driverId,
397
- initialPresentation,
398
- presentation,
399
- callbackRef,
400
- activeAnimationId
401
- )
402
- );
403
- driverRef.current = driver;
404
- }
405
-
406
- const driver = driverRef.current;
407
- useEffect(() => {
408
- const currentDriver = driverRef.current;
409
- if (!currentDriver) return undefined;
410
- const state = getDriverState(currentDriver);
411
- // Re-attach on effect replays (StrictMode/<Activity>) after a cleanup
412
- // detached this state.
413
- attachDriverState(state);
414
- return () => {
415
- cancelReanimatedAnimation(animationProgress);
416
- rejectDriverRequests(state.driverId);
417
- detachDriverState(state);
418
- };
419
- }, [animationProgress]);
420
- return driver;
421
- }
422
-
1
+ export { useSmoothClipDriver } from './drivers.native';
423
2
  export type {
424
3
  KeyframedClipAnimation,
425
4
  SpringClipAnimation,
426
5
  TimingClipAnimation,
427
- };
6
+ } from './drivers.native';