react-native-smooth-clip-view 0.3.0 → 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 -388
- package/android/build.gradle +3 -3
- package/android/proguard-rules.pro +2 -2
- package/android/src/main/cpp/SmoothClipAndroid.h +9 -12
- package/android/src/main/cpp/SmoothClipBindings.cpp +300 -704
- package/android/src/main/cpp/SmoothClipRegistry.cpp +348 -594
- package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +36 -227
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +16 -19
- package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +36 -294
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +344 -488
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +75 -77
- package/cpp/SmoothClipAnimationCurve.h +237 -346
- package/cpp/SmoothClipRegistry.h +24 -52
- package/cpp/SmoothClipSharedGeometry.h +64 -94
- package/cpp/SmoothClipVelocityTracker.h +5 -5
- package/ios/SmoothClipGeometry.h +23 -34
- package/ios/SmoothClipRegistry.mm +206 -711
- package/ios/SmoothClipTurboModule.cpp +217 -715
- package/ios/SmoothClipTurboModule.h +54 -297
- package/ios/SmoothClipView.mm +599 -505
- package/ios/SmoothClipViewRegistry.h +2 -7
- package/lib/module/NativeSmoothClipModule.js.map +1 -1
- package/lib/module/SmoothClipView.ios.js +84 -17
- package/lib/module/SmoothClipView.ios.js.map +1 -1
- package/lib/module/SmoothClipView.js +1 -1
- package/lib/module/SmoothClipView.js.map +1 -1
- package/lib/module/SmoothClipViewNativeComponent.ts +20 -26
- package/lib/module/capabilities.native.js +1 -19
- package/lib/module/capabilities.native.js.map +1 -1
- package/lib/module/capabilityTypes.js +0 -9
- package/lib/module/capabilityTypes.js.map +1 -1
- 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 +71 -51
- package/lib/module/geometry.js.map +1 -1
- package/lib/module/groupTypes.js +4 -0
- package/lib/module/{groupDriverTypes.js.map → groupTypes.js.map} +1 -1
- 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 +3 -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 +14 -23
- package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.ios.d.ts +18 -14
- package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +10 -5
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/capabilities.native.d.ts +1 -1
- package/lib/typescript/src/capabilities.native.d.ts.map +1 -1
- package/lib/typescript/src/capabilityTypes.d.ts +0 -6
- package/lib/typescript/src/capabilityTypes.d.ts.map +1 -1
- 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 +22 -12
- 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 +6 -6
- 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 +8 -3
- package/src/NativeSmoothClipModule.ts +41 -292
- package/src/SmoothClipView.ios.tsx +130 -23
- package/src/SmoothClipView.tsx +5 -1
- package/src/SmoothClipViewNativeComponent.ts +20 -26
- package/src/capabilities.native.ts +2 -52
- package/src/capabilityTypes.ts +0 -14
- 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 +118 -74
- 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 +20 -28
- package/src/nativeCompletion.ts +67 -32
- package/src/presentationCodec.ts +134 -0
- package/lib/module/driverState.js +0 -82
- 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 -4
- package/lib/module/drivers.js.map +0 -1
- package/lib/module/drivers.native.js +0 -756
- package/lib/module/drivers.native.js.map +0 -1
- package/lib/module/groupDriverTypes.js +0 -4
- package/lib/module/groupDrivers.android.js +0 -4
- package/lib/module/groupDrivers.android.js.map +0 -1
- package/lib/module/groupDrivers.ios.js +0 -4
- package/lib/module/groupDrivers.ios.js.map +0 -1
- package/lib/module/groupDrivers.js +0 -4
- package/lib/module/groupDrivers.js.map +0 -1
- package/lib/module/groupDrivers.native.js +0 -799
- package/lib/module/groupDrivers.native.js.map +0 -1
- package/lib/module/presentationProtocol.js +0 -15
- package/lib/module/presentationProtocol.js.map +0 -1
- package/lib/module/reactRequests.js +0 -56
- package/lib/module/reactRequests.js.map +0 -1
- package/lib/typescript/src/driverState.d.ts +0 -26
- package/lib/typescript/src/driverState.d.ts.map +0 -1
- package/lib/typescript/src/driverTypes.d.ts +0 -121
- 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 -3
- 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/groupDriverTypes.d.ts +0 -66
- package/lib/typescript/src/groupDriverTypes.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.android.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.android.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.ios.d.ts +0 -2
- package/lib/typescript/src/groupDrivers.ios.d.ts.map +0 -1
- package/lib/typescript/src/groupDrivers.native.d.ts +0 -4
- package/lib/typescript/src/groupDrivers.native.d.ts.map +0 -1
- package/lib/typescript/src/presentationProtocol.d.ts +0 -4
- package/lib/typescript/src/presentationProtocol.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/src/driverState.ts +0 -118
- package/src/driverTypes.ts +0 -171
- package/src/drivers.android.ts +0 -8
- package/src/drivers.ios.ts +0 -8
- package/src/drivers.native.ts +0 -1599
- package/src/drivers.ts +0 -6
- package/src/groupDriverTypes.ts +0 -117
- package/src/groupDrivers.android.ts +0 -1
- package/src/groupDrivers.ios.ts +0 -1
- package/src/groupDrivers.native.ts +0 -1189
- package/src/groupDrivers.ts +0 -1
- package/src/presentationProtocol.ts +0 -28
- package/src/reactRequests.ts +0 -76
package/src/drivers.native.ts
DELETED
|
@@ -1,1599 +0,0 @@
|
|
|
1
|
-
// The shared native driver: iOS and Android both execute this file, via the
|
|
2
|
-
// one-line re-exports in drivers.ios.ts / drivers.android.ts that Metro's
|
|
3
|
-
// platform resolution picks up from index.ts's `./drivers` import. Only the
|
|
4
|
-
// `./smoothClipNative` import below is platform-split.
|
|
5
|
-
import { useEffect, useRef } from 'react';
|
|
6
|
-
import { Platform } from 'react-native';
|
|
7
|
-
import { useSharedValue, type SharedValue } from 'react-native-reanimated';
|
|
8
|
-
import { isRNRuntime, scheduleOnRN, scheduleOnUI } from 'react-native-worklets';
|
|
9
|
-
import NativeSmoothClipModule from './smoothClipNative';
|
|
10
|
-
import { getSmoothClipCapabilities } from './capabilities';
|
|
11
|
-
import type {
|
|
12
|
-
ClipReduceMotion,
|
|
13
|
-
KeyframedClipAnimation,
|
|
14
|
-
SmoothClipAnimation,
|
|
15
|
-
SmoothClipDriver,
|
|
16
|
-
SmoothClipDriverOptions,
|
|
17
|
-
SpringClipAnimation,
|
|
18
|
-
TimingClipAnimation,
|
|
19
|
-
} from './driverTypes';
|
|
20
|
-
import {
|
|
21
|
-
allocateDriverId,
|
|
22
|
-
attachDriverState,
|
|
23
|
-
createDriverState,
|
|
24
|
-
detachDriverState,
|
|
25
|
-
getDriverState,
|
|
26
|
-
setDriverState,
|
|
27
|
-
} from './driverState';
|
|
28
|
-
import {
|
|
29
|
-
completeNativeAnimation,
|
|
30
|
-
synchronizeNativeCompletion,
|
|
31
|
-
} from './nativeCompletion';
|
|
32
|
-
import {
|
|
33
|
-
canonicalizeClipPresentation,
|
|
34
|
-
clipPresentationEquals,
|
|
35
|
-
createClipPresentation,
|
|
36
|
-
isFiniteClipPresentation,
|
|
37
|
-
type CanonicalSmoothClipPresentation,
|
|
38
|
-
type ClipGeometry,
|
|
39
|
-
type SmoothClipPresentation,
|
|
40
|
-
} from './geometry';
|
|
41
|
-
import {
|
|
42
|
-
createReactRequest,
|
|
43
|
-
rejectDriverRequests,
|
|
44
|
-
resolveReactRequest,
|
|
45
|
-
} from './reactRequests';
|
|
46
|
-
|
|
47
|
-
const INTERACTIVE = 0;
|
|
48
|
-
const NATIVE = 1;
|
|
49
|
-
|
|
50
|
-
// Only Android consumes the Reanimated start stamp (its frame-clock anchor —
|
|
51
|
-
// docs/android-frame-clock-anchor.md). iOS's TurboModule reads its declared
|
|
52
|
-
// parameters positionally and drops the trailing argument, so capturing the
|
|
53
|
-
// stamp there is a wasted native _getAnimationTimestamp() call per animateTo.
|
|
54
|
-
// Resolved once at module scope; the worklet captures the primitive.
|
|
55
|
-
const NEEDS_START_STAMP = Platform.OS === 'android';
|
|
56
|
-
const PRESENTATION_PROTOCOL_VERSION =
|
|
57
|
-
getSmoothClipCapabilities().presentationProtocolVersion;
|
|
58
|
-
|
|
59
|
-
type NativeV2HostFunctions = Readonly<{
|
|
60
|
-
setClipPresentationV2(
|
|
61
|
-
driverId: number,
|
|
62
|
-
x: number,
|
|
63
|
-
y: number,
|
|
64
|
-
width: number,
|
|
65
|
-
height: number,
|
|
66
|
-
topLeftRadius: number,
|
|
67
|
-
topRightRadius: number,
|
|
68
|
-
bottomRightRadius: number,
|
|
69
|
-
bottomLeftRadius: number,
|
|
70
|
-
curveCode: number,
|
|
71
|
-
contentTranslateX: number,
|
|
72
|
-
contentTranslateY: number,
|
|
73
|
-
contentScale: number,
|
|
74
|
-
takeOwnership: boolean,
|
|
75
|
-
overridePendingAnimation: boolean,
|
|
76
|
-
recordVelocity?: boolean
|
|
77
|
-
): void;
|
|
78
|
-
beginInteractionV2(driverId: number): readonly number[];
|
|
79
|
-
snapshotCurrentV2(driverId: number): readonly number[];
|
|
80
|
-
animateTimingV2(...args: readonly unknown[]): number;
|
|
81
|
-
animateSpringV2(...args: readonly unknown[]): number;
|
|
82
|
-
animateKeyframesV2(...args: readonly unknown[]): number;
|
|
83
|
-
animateTimingFromV2(...args: readonly unknown[]): number;
|
|
84
|
-
animateSpringFromV2(...args: readonly unknown[]): number;
|
|
85
|
-
animateKeyframesFromV2(...args: readonly unknown[]): number;
|
|
86
|
-
cancelAnimationV2(
|
|
87
|
-
driverId: number,
|
|
88
|
-
animationId: number,
|
|
89
|
-
behavior: number
|
|
90
|
-
): readonly number[];
|
|
91
|
-
}>;
|
|
92
|
-
|
|
93
|
-
const NativeSmoothClipModuleV2 =
|
|
94
|
-
NativeSmoothClipModule as unknown as Partial<NativeV2HostFunctions>;
|
|
95
|
-
|
|
96
|
-
// Widened like the animate* functions below: one trailing argument beyond the
|
|
97
|
-
// TurboModule spec — whether this write should record an `'inherit'` velocity
|
|
98
|
-
// sample. Android's JSI bindings read it (absent means record, the pre-flag
|
|
99
|
-
// behavior); the iOS TurboModule reads its declared parameters positionally
|
|
100
|
-
// and drops it, so iOS always records.
|
|
101
|
-
type WithRecordVelocity<F> = F extends (...args: infer A) => infer R
|
|
102
|
-
? (...args: [...A, recordVelocity?: boolean]) => R
|
|
103
|
-
: never;
|
|
104
|
-
const setPresentationHostFunction: WithRecordVelocity<
|
|
105
|
-
typeof NativeSmoothClipModule.setClipPresentation
|
|
106
|
-
> = NativeSmoothClipModule.setClipPresentation;
|
|
107
|
-
const beginInteractionHostFunction = NativeSmoothClipModule.beginInteraction;
|
|
108
|
-
|
|
109
|
-
// The animate* host functions take one argument beyond the TurboModule spec:
|
|
110
|
-
// the Reanimated-rule start stamp in milliseconds (see animateOnUI). Android's
|
|
111
|
-
// JSI bindings read it as an optional trailing argument; the iOS TurboModule
|
|
112
|
-
// reads its declared parameters positionally and ignores extras, so the
|
|
113
|
-
// codegen spec stays untouched and both platforms take the same call. A
|
|
114
|
-
// narrower function is assignable to the widened type, so no cast is needed.
|
|
115
|
-
type WithStartStamp<F> = F extends (...args: infer A) => infer R
|
|
116
|
-
? (...args: [...A, startedAtMs: number]) => R
|
|
117
|
-
: never;
|
|
118
|
-
const animateTimingHostFunction: WithStartStamp<
|
|
119
|
-
typeof NativeSmoothClipModule.animateTiming
|
|
120
|
-
> = NativeSmoothClipModule.animateTiming;
|
|
121
|
-
const animateSpringHostFunction: WithStartStamp<
|
|
122
|
-
typeof NativeSmoothClipModule.animateSpring
|
|
123
|
-
> = NativeSmoothClipModule.animateSpring;
|
|
124
|
-
const animateKeyframesHostFunction: WithStartStamp<
|
|
125
|
-
typeof NativeSmoothClipModule.animateKeyframes
|
|
126
|
-
> = NativeSmoothClipModule.animateKeyframes;
|
|
127
|
-
const rejectAnimationHostFunction = NativeSmoothClipModule.rejectAnimation;
|
|
128
|
-
const cancelAnimationHostFunction = NativeSmoothClipModule.cancelAnimation;
|
|
129
|
-
const destroyDriverHostFunction = NativeSmoothClipModule.destroyDriver;
|
|
130
|
-
const setPresentationV2HostFunction =
|
|
131
|
-
NativeSmoothClipModuleV2.setClipPresentationV2;
|
|
132
|
-
const beginInteractionV2HostFunction =
|
|
133
|
-
NativeSmoothClipModuleV2.beginInteractionV2;
|
|
134
|
-
const snapshotCurrentV2HostFunction =
|
|
135
|
-
NativeSmoothClipModuleV2.snapshotCurrentV2;
|
|
136
|
-
const animateTimingV2HostFunction = NativeSmoothClipModuleV2.animateTimingV2;
|
|
137
|
-
const animateSpringV2HostFunction = NativeSmoothClipModuleV2.animateSpringV2;
|
|
138
|
-
const animateKeyframesV2HostFunction =
|
|
139
|
-
NativeSmoothClipModuleV2.animateKeyframesV2;
|
|
140
|
-
const animateTimingFromV2HostFunction =
|
|
141
|
-
NativeSmoothClipModuleV2.animateTimingFromV2;
|
|
142
|
-
const animateSpringFromV2HostFunction =
|
|
143
|
-
NativeSmoothClipModuleV2.animateSpringFromV2;
|
|
144
|
-
const animateKeyframesFromV2HostFunction =
|
|
145
|
-
NativeSmoothClipModuleV2.animateKeyframesFromV2;
|
|
146
|
-
const cancelAnimationV2HostFunction =
|
|
147
|
-
NativeSmoothClipModuleV2.cancelAnimationV2;
|
|
148
|
-
|
|
149
|
-
// Workaround for react-native-worklets 0.10.0 (fixed upstream in >=0.10.1,
|
|
150
|
-
// which drops the registry/proxy design; kept while the peer range still
|
|
151
|
-
// allows 0.10.0 consumers): the RN-side serialization cache
|
|
152
|
-
// hands out one remote-function registry id per function forever, but the
|
|
153
|
-
// registry entry is deleted as soon as any transient UI-side proxy of that
|
|
154
|
-
// function is garbage collected. A later scheduleOnRN for the same function
|
|
155
|
-
// then reads `undefined` from the registry and aborts in Value::getObject.
|
|
156
|
-
// Pinning one UI-side reference for the app lifetime keeps the proxy — and
|
|
157
|
-
// with it the registry entry — alive. Remove once the peer range requires
|
|
158
|
-
// worklets >=0.10.1.
|
|
159
|
-
scheduleOnUI((resolver: typeof resolveReactRequest) => {
|
|
160
|
-
'worklet';
|
|
161
|
-
(globalThis as Record<string, unknown>).__smoothClipPinnedResolver = resolver;
|
|
162
|
-
}, resolveReactRequest);
|
|
163
|
-
|
|
164
|
-
function reduceMotionCode(value: ClipReduceMotion): number {
|
|
165
|
-
switch (value) {
|
|
166
|
-
case 'always':
|
|
167
|
-
return 1;
|
|
168
|
-
case 'never':
|
|
169
|
-
return 2;
|
|
170
|
-
default:
|
|
171
|
-
return 0;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function animationIsFinite(
|
|
176
|
-
animation: SmoothClipAnimation,
|
|
177
|
-
strictV2: boolean
|
|
178
|
-
): boolean {
|
|
179
|
-
'worklet';
|
|
180
|
-
if (
|
|
181
|
-
animation.from !== undefined &&
|
|
182
|
-
!isFiniteClipPresentation(animation.from)
|
|
183
|
-
) {
|
|
184
|
-
// Silently dropping an invalid explicit start would re-open the exact
|
|
185
|
-
// stale-start handoff `from` exists to close — reject the whole call.
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
if (animation.type === 'timing') {
|
|
189
|
-
const [x1, , x2] = animation.controlPoints;
|
|
190
|
-
return (
|
|
191
|
-
Number.isFinite(animation.duration) &&
|
|
192
|
-
(!strictV2 || animation.duration >= 0) &&
|
|
193
|
-
animation.controlPoints.every(Number.isFinite) &&
|
|
194
|
-
// A cubic-bezier easing is only defined for x within [0,1] (the CSS /
|
|
195
|
-
// CoreAnimation / Reanimated Easing.bezier contract). Out-of-range x
|
|
196
|
-
// makes the Android parameter solve meaningless while CoreAnimation
|
|
197
|
-
// silently clamps — reject like any other invalid input instead of
|
|
198
|
-
// diverging per platform.
|
|
199
|
-
(!strictV2 || (x1 >= 0 && x1 <= 1 && x2 >= 0 && x2 <= 1))
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
if (animation.type === 'spring') {
|
|
203
|
-
const mass = animation.mass ?? 1;
|
|
204
|
-
const stiffness = animation.stiffness ?? 100;
|
|
205
|
-
const damping = animation.damping ?? 10;
|
|
206
|
-
const initialVelocity =
|
|
207
|
-
animation.initialVelocity === 'inherit'
|
|
208
|
-
? 0
|
|
209
|
-
: (animation.initialVelocity ?? 0);
|
|
210
|
-
return (
|
|
211
|
-
[mass, stiffness, damping, initialVelocity].every(Number.isFinite) &&
|
|
212
|
-
(!strictV2 || (mass > 0 && stiffness > 0 && damping >= 0))
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
// Keyframe frames validate inside flattenFiniteKeyframes — one traversal
|
|
216
|
-
// both checks and flattens, instead of walking every frame twice. The type
|
|
217
|
-
// check is statically always-true for the TS union but load-bearing for JS
|
|
218
|
-
// callers: an unrecognized type must reject here, or it would skip the
|
|
219
|
-
// keyframe gate entirely and hand native a null frames array.
|
|
220
|
-
return (
|
|
221
|
-
animation.type === 'keyframes' && (!strictV2 || animation.duration >= 0)
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function presentationFromNative(
|
|
226
|
-
values: ReadonlyArray<number>,
|
|
227
|
-
fallback: CanonicalSmoothClipPresentation,
|
|
228
|
-
offset = 0
|
|
229
|
-
): CanonicalSmoothClipPresentation {
|
|
230
|
-
'worklet';
|
|
231
|
-
if (values.length >= offset + 12) {
|
|
232
|
-
const presentation = canonicalizeClipPresentation({
|
|
233
|
-
clip: {
|
|
234
|
-
x: values[offset] as number,
|
|
235
|
-
y: values[offset + 1] as number,
|
|
236
|
-
width: values[offset + 2] as number,
|
|
237
|
-
height: values[offset + 3] as number,
|
|
238
|
-
radius: 0,
|
|
239
|
-
topLeftRadius: values[offset + 4] as number,
|
|
240
|
-
topRightRadius: values[offset + 5] as number,
|
|
241
|
-
bottomRightRadius: values[offset + 6] as number,
|
|
242
|
-
bottomLeftRadius: values[offset + 7] as number,
|
|
243
|
-
curve: values[offset + 8] === 1 ? 'continuous' : 'circular',
|
|
244
|
-
},
|
|
245
|
-
contentTranslateX: values[offset + 9] as number,
|
|
246
|
-
contentTranslateY: values[offset + 10] as number,
|
|
247
|
-
contentScale: values[offset + 11] as number,
|
|
248
|
-
});
|
|
249
|
-
return presentation ?? fallback;
|
|
250
|
-
}
|
|
251
|
-
if (values.length < offset + 7) return fallback;
|
|
252
|
-
const presentation = canonicalizeClipPresentation({
|
|
253
|
-
clip: {
|
|
254
|
-
x: values[offset] as number,
|
|
255
|
-
y: values[offset + 1] as number,
|
|
256
|
-
width: values[offset + 2] as number,
|
|
257
|
-
height: values[offset + 3] as number,
|
|
258
|
-
radius: values[offset + 4] as number,
|
|
259
|
-
},
|
|
260
|
-
contentTranslateX: values[offset + 5] as number,
|
|
261
|
-
contentTranslateY: values[offset + 6] as number,
|
|
262
|
-
});
|
|
263
|
-
return presentation ?? fallback;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function curveCode(presentation: CanonicalSmoothClipPresentation): number {
|
|
267
|
-
'worklet';
|
|
268
|
-
return presentation.clip.curve === 'continuous' ? 1 : 0;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function isV1Compatible(
|
|
272
|
-
presentation: CanonicalSmoothClipPresentation
|
|
273
|
-
): boolean {
|
|
274
|
-
'worklet';
|
|
275
|
-
const clip = presentation.clip;
|
|
276
|
-
return (
|
|
277
|
-
clip.curve === 'circular' &&
|
|
278
|
-
clip.topLeftRadius === clip.radius &&
|
|
279
|
-
clip.topRightRadius === clip.radius &&
|
|
280
|
-
clip.bottomRightRadius === clip.radius &&
|
|
281
|
-
clip.bottomLeftRadius === clip.radius &&
|
|
282
|
-
presentation.contentScale === 1
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function springScaleIsProvablyPositive(
|
|
287
|
-
start: CanonicalSmoothClipPresentation,
|
|
288
|
-
target: CanonicalSmoothClipPresentation,
|
|
289
|
-
animation: SpringClipAnimation
|
|
290
|
-
): boolean {
|
|
291
|
-
'worklet';
|
|
292
|
-
if (start.contentScale === target.contentScale) return true;
|
|
293
|
-
|
|
294
|
-
// A zero-velocity critically/over-damped step is monotonic, so its sampled
|
|
295
|
-
// scale remains between two already-positive endpoints. Inherited or
|
|
296
|
-
// under-damped configurations can overshoot; callers must compile those
|
|
297
|
-
// plans to exact keyframes instead of risking a non-positive transform.
|
|
298
|
-
const mass = animation.mass ?? 1;
|
|
299
|
-
const stiffness = animation.stiffness ?? 100;
|
|
300
|
-
const damping = animation.damping ?? 10;
|
|
301
|
-
return (
|
|
302
|
-
animation.initialVelocity === 0 && damping * damping >= 4 * mass * stiffness
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Validates and flattens the keyframe list in one traversal (the flat number
|
|
307
|
-
// array is the wire format the native side reads back positionally). Returns
|
|
308
|
-
// null when any frame is invalid — same rejection contract the two-pass
|
|
309
|
-
// validate-then-flatten had.
|
|
310
|
-
function flattenFiniteKeyframes(
|
|
311
|
-
target: CanonicalSmoothClipPresentation,
|
|
312
|
-
animation: KeyframedClipAnimation
|
|
313
|
-
): {
|
|
314
|
-
frames: number[];
|
|
315
|
-
canonicalFrames: CanonicalSmoothClipPresentation[];
|
|
316
|
-
} | null {
|
|
317
|
-
'worklet';
|
|
318
|
-
if (!Number.isFinite(animation.duration) || animation.frames.length < 2) {
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
const values: number[] = [];
|
|
322
|
-
const canonicalFrames: CanonicalSmoothClipPresentation[] = [];
|
|
323
|
-
let previousOffset = -1;
|
|
324
|
-
for (const frame of animation.frames) {
|
|
325
|
-
const { offset } = frame;
|
|
326
|
-
const presentation = canonicalizeClipPresentation(frame.presentation);
|
|
327
|
-
if (
|
|
328
|
-
!Number.isFinite(offset) ||
|
|
329
|
-
offset < 0 ||
|
|
330
|
-
offset > 1 ||
|
|
331
|
-
offset <= previousOffset ||
|
|
332
|
-
presentation === null ||
|
|
333
|
-
presentation.clip.curve !== target.clip.curve
|
|
334
|
-
) {
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
previousOffset = offset;
|
|
338
|
-
canonicalFrames.push(presentation);
|
|
339
|
-
const { clip, contentTranslateX, contentTranslateY, contentScale } =
|
|
340
|
-
presentation;
|
|
341
|
-
values.push(
|
|
342
|
-
offset,
|
|
343
|
-
clip.x,
|
|
344
|
-
clip.y,
|
|
345
|
-
clip.width,
|
|
346
|
-
clip.height,
|
|
347
|
-
clip.topLeftRadius,
|
|
348
|
-
clip.topRightRadius,
|
|
349
|
-
clip.bottomRightRadius,
|
|
350
|
-
clip.bottomLeftRadius,
|
|
351
|
-
curveCode(presentation),
|
|
352
|
-
contentTranslateX,
|
|
353
|
-
contentTranslateY,
|
|
354
|
-
contentScale
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
if (
|
|
358
|
-
animation.frames[0]?.offset !== 0 ||
|
|
359
|
-
previousOffset !== 1 ||
|
|
360
|
-
!clipPresentationEquals(
|
|
361
|
-
canonicalFrames[canonicalFrames.length - 1] ?? null,
|
|
362
|
-
target
|
|
363
|
-
)
|
|
364
|
-
) {
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
return { frames: values, canonicalFrames };
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function flattenV1Keyframes(
|
|
371
|
-
frames: readonly CanonicalSmoothClipPresentation[],
|
|
372
|
-
offsets: readonly number[]
|
|
373
|
-
): number[] {
|
|
374
|
-
'worklet';
|
|
375
|
-
const values: number[] = [];
|
|
376
|
-
for (let index = 0; index < frames.length; index += 1) {
|
|
377
|
-
const presentation = frames[index];
|
|
378
|
-
const offset = offsets[index];
|
|
379
|
-
if (presentation === undefined || offset === undefined) continue;
|
|
380
|
-
values.push(
|
|
381
|
-
offset,
|
|
382
|
-
presentation.clip.x,
|
|
383
|
-
presentation.clip.y,
|
|
384
|
-
presentation.clip.width,
|
|
385
|
-
presentation.clip.height,
|
|
386
|
-
presentation.clip.radius,
|
|
387
|
-
presentation.contentTranslateX,
|
|
388
|
-
presentation.contentTranslateY
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
return values;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function uiOnly(): never {
|
|
395
|
-
'worklet';
|
|
396
|
-
throw new Error(
|
|
397
|
-
'[SmoothClipView] driver.ui methods must run on the UI runtime. Use driver.react from React code.'
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export function useSmoothClipDriver(
|
|
402
|
-
initialValue: ClipGeometry | SmoothClipPresentation,
|
|
403
|
-
options: SmoothClipDriverOptions = {}
|
|
404
|
-
): SmoothClipDriver {
|
|
405
|
-
const requestedInitialPresentation =
|
|
406
|
-
'clip' in initialValue
|
|
407
|
-
? initialValue
|
|
408
|
-
: createClipPresentation(initialValue);
|
|
409
|
-
const initialPresentation = canonicalizeClipPresentation(
|
|
410
|
-
requestedInitialPresentation
|
|
411
|
-
);
|
|
412
|
-
if (initialPresentation === null) {
|
|
413
|
-
throw new Error('[SmoothClipView] Initial presentation must be finite.');
|
|
414
|
-
}
|
|
415
|
-
const presentation =
|
|
416
|
-
useSharedValue<SmoothClipPresentation>(initialPresentation);
|
|
417
|
-
const ownership = useSharedValue(INTERACTIVE);
|
|
418
|
-
const suppressDeliveries = useSharedValue(0);
|
|
419
|
-
const activeAnimationId = useSharedValue(0);
|
|
420
|
-
// Non-zero after a setScalars hot write: driver.presentation is then stale,
|
|
421
|
-
// so animateTo must start from the native registry's latest value instead
|
|
422
|
-
// of snapping back to the stale SharedValue.
|
|
423
|
-
const scalarsStale = useSharedValue(0);
|
|
424
|
-
// Non-zero between the effect cleanup's native teardown and the next effect
|
|
425
|
-
// run. Native cannot tell "this driver was never seeded" from "this driver
|
|
426
|
-
// was destroyed and erased" — both are a missing registry entry — so the
|
|
427
|
-
// animation entry points would happily create a fresh state for a call that
|
|
428
|
-
// arrives after the hook is gone, leaving a latch that no view starts and no
|
|
429
|
-
// destroy cancels. Only the UI side knows the difference, so it is decided
|
|
430
|
-
// here.
|
|
431
|
-
const disposed = useSharedValue(0);
|
|
432
|
-
const callbackRef = useRef(options.onAnimationComplete);
|
|
433
|
-
const driverRef = useRef<SmoothClipDriver | null>(null);
|
|
434
|
-
callbackRef.current = options.onAnimationComplete;
|
|
435
|
-
|
|
436
|
-
if (driverRef.current === null) {
|
|
437
|
-
const driverId = allocateDriverId();
|
|
438
|
-
const reduceMotion = reduceMotionCode(options.reduceMotion ?? 'system');
|
|
439
|
-
// Captured once, like reduceMotion. Only the per-frame setScalars stream
|
|
440
|
-
// consults it; set(), the fused animateTo `from` seed, and the effect's
|
|
441
|
-
// declarative deliveries always record, so every once-per-call channel
|
|
442
|
-
// keeps its pre-flag 'inherit' behavior.
|
|
443
|
-
const velocityTracking = options.velocityTracking === true;
|
|
444
|
-
const seedPresentation = (next: CanonicalSmoothClipPresentation) => {
|
|
445
|
-
'worklet';
|
|
446
|
-
if (clipPresentationEquals(presentation.value, next)) return;
|
|
447
|
-
suppressDeliveries.value += 1;
|
|
448
|
-
presentation.value = next;
|
|
449
|
-
// The listener runs synchronously inside that assignment, so the credit
|
|
450
|
-
// is either spent by now or was never spendable. deliver() returns before
|
|
451
|
-
// the decrement whenever the value dedupes against `last` — which happens
|
|
452
|
-
// exactly when native already holds it, e.g. freezing a latch back to the
|
|
453
|
-
// value it started from. An unspent credit would then swallow the next
|
|
454
|
-
// real declarative write and leave native a geometry behind for good.
|
|
455
|
-
if (suppressDeliveries.value > 0) suppressDeliveries.value -= 1;
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const beginOnUI = (): CanonicalSmoothClipPresentation => {
|
|
459
|
-
'worklet';
|
|
460
|
-
const fallback =
|
|
461
|
-
canonicalizeClipPresentation(presentation.value) ?? initialPresentation;
|
|
462
|
-
if (disposed.value !== 0) return fallback;
|
|
463
|
-
const current = presentationFromNative(
|
|
464
|
-
PRESENTATION_PROTOCOL_VERSION === 2 && beginInteractionV2HostFunction
|
|
465
|
-
? beginInteractionV2HostFunction(driverId)
|
|
466
|
-
: beginInteractionHostFunction(driverId),
|
|
467
|
-
fallback
|
|
468
|
-
);
|
|
469
|
-
activeAnimationId.value = 0;
|
|
470
|
-
ownership.value = INTERACTIVE;
|
|
471
|
-
scalarsStale.value = 0;
|
|
472
|
-
seedPresentation(current);
|
|
473
|
-
return current;
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const setOnUI = (next: SmoothClipPresentation): void => {
|
|
477
|
-
'worklet';
|
|
478
|
-
const canonical = canonicalizeClipPresentation(next);
|
|
479
|
-
if (disposed.value !== 0 || canonical === null) return;
|
|
480
|
-
activeAnimationId.value = 0;
|
|
481
|
-
ownership.value = INTERACTIVE;
|
|
482
|
-
scalarsStale.value = 0;
|
|
483
|
-
const { clip, contentTranslateX, contentTranslateY, contentScale } =
|
|
484
|
-
canonical;
|
|
485
|
-
// Always records: only the per-frame setScalars hot path is gated by
|
|
486
|
-
// velocityTracking. set() already pays full validation and a SharedValue
|
|
487
|
-
// seed per call, so the sample recording is noise here.
|
|
488
|
-
if (
|
|
489
|
-
PRESENTATION_PROTOCOL_VERSION === 2 &&
|
|
490
|
-
setPresentationV2HostFunction
|
|
491
|
-
) {
|
|
492
|
-
setPresentationV2HostFunction(
|
|
493
|
-
driverId,
|
|
494
|
-
clip.x,
|
|
495
|
-
clip.y,
|
|
496
|
-
clip.width,
|
|
497
|
-
clip.height,
|
|
498
|
-
clip.topLeftRadius,
|
|
499
|
-
clip.topRightRadius,
|
|
500
|
-
clip.bottomRightRadius,
|
|
501
|
-
clip.bottomLeftRadius,
|
|
502
|
-
curveCode(canonical),
|
|
503
|
-
contentTranslateX,
|
|
504
|
-
contentTranslateY,
|
|
505
|
-
contentScale,
|
|
506
|
-
true,
|
|
507
|
-
false,
|
|
508
|
-
true
|
|
509
|
-
);
|
|
510
|
-
} else if (isV1Compatible(canonical)) {
|
|
511
|
-
setPresentationHostFunction(
|
|
512
|
-
driverId,
|
|
513
|
-
clip.x,
|
|
514
|
-
clip.y,
|
|
515
|
-
clip.width,
|
|
516
|
-
clip.height,
|
|
517
|
-
clip.radius,
|
|
518
|
-
contentTranslateX,
|
|
519
|
-
contentTranslateY,
|
|
520
|
-
true,
|
|
521
|
-
false
|
|
522
|
-
);
|
|
523
|
-
} else {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
seedPresentation(canonical);
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
const setScalarsOnUI = (
|
|
530
|
-
x: number,
|
|
531
|
-
y: number,
|
|
532
|
-
width: number,
|
|
533
|
-
height: number,
|
|
534
|
-
radius: number,
|
|
535
|
-
contentTranslateX: number,
|
|
536
|
-
contentTranslateY: number,
|
|
537
|
-
overridePendingAnimation = false,
|
|
538
|
-
forceRecordVelocity = false
|
|
539
|
-
): void => {
|
|
540
|
-
'worklet';
|
|
541
|
-
// Resolved in the body, not as a default-parameter initializer: the
|
|
542
|
-
// worklets Babel plugin only captures closure variables referenced
|
|
543
|
-
// inside the function body, so `= velocityTracking` up in the parameter
|
|
544
|
-
// list would throw "Property 'velocityTracking' doesn't exist" on the
|
|
545
|
-
// UI runtime.
|
|
546
|
-
const recordVelocity = forceRecordVelocity || velocityTracking;
|
|
547
|
-
// Number.isFinite (not a fused arithmetic gate): it also rejects
|
|
548
|
-
// non-number types, which arithmetic would coerce past the gate and
|
|
549
|
-
// into a UI-runtime throw from the binding's asNumber. The gate must
|
|
550
|
-
// stay on this side of the bridge: the native binding drops non-finite
|
|
551
|
-
// writes silently, and flipping the ownership SharedValues below for a
|
|
552
|
-
// write native never applied would let the deliver listener record
|
|
553
|
-
// values native has not observed — a later identical declarative write
|
|
554
|
-
// then dedupes against them and is swallowed for good.
|
|
555
|
-
if (
|
|
556
|
-
disposed.value !== 0 ||
|
|
557
|
-
!Number.isFinite(x) ||
|
|
558
|
-
!Number.isFinite(y) ||
|
|
559
|
-
!Number.isFinite(width) ||
|
|
560
|
-
!Number.isFinite(height) ||
|
|
561
|
-
!Number.isFinite(radius) ||
|
|
562
|
-
!Number.isFinite(contentTranslateX) ||
|
|
563
|
-
!Number.isFinite(contentTranslateY)
|
|
564
|
-
) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
if (activeAnimationId.value !== 0) activeAnimationId.value = 0;
|
|
568
|
-
if (ownership.value !== INTERACTIVE) ownership.value = INTERACTIVE;
|
|
569
|
-
if (scalarsStale.value === 0) scalarsStale.value = 1;
|
|
570
|
-
setPresentationHostFunction(
|
|
571
|
-
driverId,
|
|
572
|
-
x,
|
|
573
|
-
y,
|
|
574
|
-
width,
|
|
575
|
-
height,
|
|
576
|
-
radius,
|
|
577
|
-
contentTranslateX,
|
|
578
|
-
contentTranslateY,
|
|
579
|
-
true,
|
|
580
|
-
overridePendingAnimation,
|
|
581
|
-
recordVelocity
|
|
582
|
-
);
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
const setPresentationScalarsOnUI = (
|
|
586
|
-
x: number,
|
|
587
|
-
y: number,
|
|
588
|
-
width: number,
|
|
589
|
-
height: number,
|
|
590
|
-
topLeftRadius: number,
|
|
591
|
-
topRightRadius: number,
|
|
592
|
-
bottomRightRadius: number,
|
|
593
|
-
bottomLeftRadius: number,
|
|
594
|
-
nextCurveCode: number,
|
|
595
|
-
contentTranslateX: number,
|
|
596
|
-
contentTranslateY: number,
|
|
597
|
-
contentScale: number,
|
|
598
|
-
overridePendingAnimation = false,
|
|
599
|
-
forceRecordVelocity = false
|
|
600
|
-
): void => {
|
|
601
|
-
'worklet';
|
|
602
|
-
const values = [
|
|
603
|
-
x,
|
|
604
|
-
y,
|
|
605
|
-
width,
|
|
606
|
-
height,
|
|
607
|
-
topLeftRadius,
|
|
608
|
-
topRightRadius,
|
|
609
|
-
bottomRightRadius,
|
|
610
|
-
bottomLeftRadius,
|
|
611
|
-
nextCurveCode,
|
|
612
|
-
contentTranslateX,
|
|
613
|
-
contentTranslateY,
|
|
614
|
-
contentScale,
|
|
615
|
-
];
|
|
616
|
-
if (
|
|
617
|
-
disposed.value !== 0 ||
|
|
618
|
-
!values.every(Number.isFinite) ||
|
|
619
|
-
(nextCurveCode !== 0 && nextCurveCode !== 1) ||
|
|
620
|
-
contentScale <= 0
|
|
621
|
-
) {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
const recordVelocity = forceRecordVelocity || velocityTracking;
|
|
625
|
-
if (
|
|
626
|
-
PRESENTATION_PROTOCOL_VERSION !== 2 ||
|
|
627
|
-
!setPresentationV2HostFunction
|
|
628
|
-
) {
|
|
629
|
-
if (
|
|
630
|
-
nextCurveCode === 0 &&
|
|
631
|
-
contentScale === 1 &&
|
|
632
|
-
topLeftRadius === topRightRadius &&
|
|
633
|
-
topLeftRadius === bottomRightRadius &&
|
|
634
|
-
topLeftRadius === bottomLeftRadius
|
|
635
|
-
) {
|
|
636
|
-
setScalarsOnUI(
|
|
637
|
-
x,
|
|
638
|
-
y,
|
|
639
|
-
width,
|
|
640
|
-
height,
|
|
641
|
-
topLeftRadius,
|
|
642
|
-
contentTranslateX,
|
|
643
|
-
contentTranslateY,
|
|
644
|
-
overridePendingAnimation,
|
|
645
|
-
forceRecordVelocity
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
if (activeAnimationId.value !== 0) activeAnimationId.value = 0;
|
|
651
|
-
if (ownership.value !== INTERACTIVE) ownership.value = INTERACTIVE;
|
|
652
|
-
if (scalarsStale.value === 0) scalarsStale.value = 1;
|
|
653
|
-
setPresentationV2HostFunction(
|
|
654
|
-
driverId,
|
|
655
|
-
x,
|
|
656
|
-
y,
|
|
657
|
-
width,
|
|
658
|
-
height,
|
|
659
|
-
topLeftRadius,
|
|
660
|
-
topRightRadius,
|
|
661
|
-
bottomRightRadius,
|
|
662
|
-
bottomLeftRadius,
|
|
663
|
-
nextCurveCode,
|
|
664
|
-
contentTranslateX,
|
|
665
|
-
contentTranslateY,
|
|
666
|
-
contentScale,
|
|
667
|
-
true,
|
|
668
|
-
overridePendingAnimation,
|
|
669
|
-
recordVelocity
|
|
670
|
-
);
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
const animateOnUI = (
|
|
674
|
-
target: SmoothClipPresentation,
|
|
675
|
-
animation: SmoothClipAnimation
|
|
676
|
-
): number => {
|
|
677
|
-
'worklet';
|
|
678
|
-
// Cleanup owns the terminal state. Check it before validation so even an
|
|
679
|
-
// invalid stale call cannot mint a native rejection id/completion against
|
|
680
|
-
// a driver whose listener and registry entry are already gone.
|
|
681
|
-
if (disposed.value !== 0) return 0;
|
|
682
|
-
const canonicalTarget = canonicalizeClipPresentation(target);
|
|
683
|
-
const canonicalFrom =
|
|
684
|
-
animation.from === undefined
|
|
685
|
-
? undefined
|
|
686
|
-
: canonicalizeClipPresentation(animation.from);
|
|
687
|
-
const keyframeResult =
|
|
688
|
-
animation.type === 'keyframes'
|
|
689
|
-
? canonicalTarget === null
|
|
690
|
-
? null
|
|
691
|
-
: flattenFiniteKeyframes(canonicalTarget, animation)
|
|
692
|
-
: null;
|
|
693
|
-
if (
|
|
694
|
-
canonicalTarget === null ||
|
|
695
|
-
(animation.from !== undefined && canonicalFrom === null) ||
|
|
696
|
-
(animation.type === 'keyframes' && keyframeResult === null)
|
|
697
|
-
) {
|
|
698
|
-
return rejectAnimationHostFunction(driverId);
|
|
699
|
-
}
|
|
700
|
-
const resolvedFrom = canonicalFrom ?? undefined;
|
|
701
|
-
const v1Compatible =
|
|
702
|
-
isV1Compatible(canonicalTarget) &&
|
|
703
|
-
(resolvedFrom === undefined || isV1Compatible(resolvedFrom)) &&
|
|
704
|
-
(animation.type !== 'keyframes' ||
|
|
705
|
-
(keyframeResult?.canonicalFrames.every(isV1Compatible) ?? false));
|
|
706
|
-
// V1-representable plans deliberately stay on the original seven-scalar
|
|
707
|
-
// bridge even when V2 is installed. Besides preserving bridge shape,
|
|
708
|
-
// this retains V1's finite-value/clamping and host-normalization rules.
|
|
709
|
-
const useV2 = !v1Compatible;
|
|
710
|
-
if (!animationIsFinite(animation, useV2)) {
|
|
711
|
-
return rejectAnimationHostFunction(driverId);
|
|
712
|
-
}
|
|
713
|
-
const hasV2Protocol =
|
|
714
|
-
PRESENTATION_PROTOCOL_VERSION === 2 &&
|
|
715
|
-
snapshotCurrentV2HostFunction !== undefined &&
|
|
716
|
-
animateTimingV2HostFunction !== undefined &&
|
|
717
|
-
animateSpringV2HostFunction !== undefined &&
|
|
718
|
-
animateKeyframesV2HostFunction !== undefined;
|
|
719
|
-
const hasAtomicFromMethod =
|
|
720
|
-
resolvedFrom === undefined ||
|
|
721
|
-
(animation.type === 'timing'
|
|
722
|
-
? animateTimingFromV2HostFunction !== undefined
|
|
723
|
-
: animation.type === 'spring'
|
|
724
|
-
? animateSpringFromV2HostFunction !== undefined
|
|
725
|
-
: animateKeyframesFromV2HostFunction !== undefined);
|
|
726
|
-
// This is intentionally before sampling or any ownership/group/
|
|
727
|
-
// presentation mutation. New JS against old native must fail closed,
|
|
728
|
-
// and an explicit V2 start must never degrade to set()+animate().
|
|
729
|
-
if (useV2 && (!hasV2Protocol || !hasAtomicFromMethod)) {
|
|
730
|
-
return rejectAnimationHostFunction(driverId);
|
|
731
|
-
}
|
|
732
|
-
if (
|
|
733
|
-
useV2 &&
|
|
734
|
-
resolvedFrom !== undefined &&
|
|
735
|
-
animation.type === 'keyframes' &&
|
|
736
|
-
!clipPresentationEquals(
|
|
737
|
-
keyframeResult?.canonicalFrames[0] ?? null,
|
|
738
|
-
resolvedFrom
|
|
739
|
-
)
|
|
740
|
-
) {
|
|
741
|
-
return rejectAnimationHostFunction(driverId);
|
|
742
|
-
}
|
|
743
|
-
const currentPresentation =
|
|
744
|
-
canonicalizeClipPresentation(presentation.value) ?? initialPresentation;
|
|
745
|
-
const sampledStart =
|
|
746
|
-
resolvedFrom ??
|
|
747
|
-
(useV2
|
|
748
|
-
? presentationFromNative(
|
|
749
|
-
snapshotCurrentV2HostFunction!(driverId),
|
|
750
|
-
currentPresentation
|
|
751
|
-
)
|
|
752
|
-
: currentPresentation);
|
|
753
|
-
// setPresentationScalars intentionally leaves the public SharedValue
|
|
754
|
-
// stale to keep the hot path allocation-free. Validate an implicit
|
|
755
|
-
// retarget against the sampled native-visible start, not that stale JS
|
|
756
|
-
// value; otherwise a streamed curve transition cannot settle natively on
|
|
757
|
-
// its new (now stable) curve.
|
|
758
|
-
if (canonicalTarget.clip.curve !== sampledStart.clip.curve) {
|
|
759
|
-
return rejectAnimationHostFunction(driverId);
|
|
760
|
-
}
|
|
761
|
-
if (
|
|
762
|
-
useV2 &&
|
|
763
|
-
animation.type === 'spring' &&
|
|
764
|
-
!springScaleIsProvablyPositive(sampledStart, canonicalTarget, animation)
|
|
765
|
-
) {
|
|
766
|
-
return rejectAnimationHostFunction(driverId);
|
|
767
|
-
}
|
|
768
|
-
// Past the hook's cleanup the native entry is gone for good, and an
|
|
769
|
-
// interactive start here would recreate it as a latch nothing can ever
|
|
770
|
-
// start or cancel. Reject before any side effect — not via
|
|
771
|
-
// rejectAnimation, which would mint an id and a completion for a driver
|
|
772
|
-
// whose JS state is already detached. This is the "unsupported dispatch"
|
|
773
|
-
// arm of the documented 0 contract.
|
|
774
|
-
const from = resolvedFrom;
|
|
775
|
-
if (from !== undefined && !useV2) {
|
|
776
|
-
// Fused hot write: exactly setScalars(from…) issued immediately
|
|
777
|
-
// before the handoff, so native's latest value — the animation
|
|
778
|
-
// start below — is the caller's explicit presentation. Also
|
|
779
|
-
// re-grabs from a running animation, which the implicit
|
|
780
|
-
// interactive-start path would silently skip. Always records a
|
|
781
|
-
// velocity sample regardless of velocityTracking: this is one write
|
|
782
|
-
// per animateTo, not the per-frame stream the flag exists to keep
|
|
783
|
-
// cheap, and an unrecorded seed would invalidate the history a
|
|
784
|
-
// set()-driven drag just recorded (the tracker's coalesce and
|
|
785
|
-
// identical-re-record rules were designed for exactly this write).
|
|
786
|
-
setScalarsOnUI(
|
|
787
|
-
from.clip.x,
|
|
788
|
-
from.clip.y,
|
|
789
|
-
from.clip.width,
|
|
790
|
-
from.clip.height,
|
|
791
|
-
from.clip.radius,
|
|
792
|
-
from.contentTranslateX,
|
|
793
|
-
from.contentTranslateY,
|
|
794
|
-
true,
|
|
795
|
-
true
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// After a setScalars hot write the SharedValue no longer matches what
|
|
800
|
-
// is on screen; passing it as the start would snap the animation back.
|
|
801
|
-
// Without an interactive start, native resolves the start from its own
|
|
802
|
-
// latest value instead.
|
|
803
|
-
const scalarsWereStale = scalarsStale.value !== 0;
|
|
804
|
-
const hasInteractiveStart =
|
|
805
|
-
ownership.value === INTERACTIVE && !scalarsWereStale;
|
|
806
|
-
const start =
|
|
807
|
-
canonicalizeClipPresentation(presentation.value) ?? initialPresentation;
|
|
808
|
-
// V1 preserves the old optimistic SharedValue ownership transition.
|
|
809
|
-
// V2 commits it only after native has atomically accepted the plan.
|
|
810
|
-
if (!useV2) {
|
|
811
|
-
ownership.value = NATIVE;
|
|
812
|
-
presentation.value = canonicalTarget;
|
|
813
|
-
scalarsStale.value = 0;
|
|
814
|
-
}
|
|
815
|
-
const dispatchStart = useV2 && from !== undefined ? from : start;
|
|
816
|
-
const startClip = dispatchStart.clip;
|
|
817
|
-
const targetClip = canonicalTarget.clip;
|
|
818
|
-
// Reanimated stamps a parallel animation's t0 on this same runtime as
|
|
819
|
-
// `__frameTimestamp || _getAnimationTimestamp()` (valueSetter.ts).
|
|
820
|
-
// Capturing the identical value here and handing it to the integrator
|
|
821
|
-
// keeps clip and content phase-locked in every branch — including a
|
|
822
|
-
// start issued from CALLBACK_INPUT (batched gesture moves), where the
|
|
823
|
-
// dispatching frame's stamp is EARLIER than the call: the native min()
|
|
824
|
-
// anchor alone would adopt that frame stamp while Reanimated, outside
|
|
825
|
-
// its rAF flush where __frameTimestamp is cleared, keeps the wall
|
|
826
|
-
// clock — a lead lasting the whole animation. NaN when the globals are
|
|
827
|
-
// absent (tests, non-worklets runtimes): native then falls back to its
|
|
828
|
-
// own clock plus the min() anchor, the pre-stamp behavior. On iOS the
|
|
829
|
-
// stamp is always NaN: Core Animation anchors at commit time and the
|
|
830
|
-
// TurboModule discards the argument, so the capture would only cost.
|
|
831
|
-
const workletGlobal = globalThis as {
|
|
832
|
-
__frameTimestamp?: number;
|
|
833
|
-
_getAnimationTimestamp?: () => number;
|
|
834
|
-
};
|
|
835
|
-
const startedAtMs = !NEEDS_START_STAMP
|
|
836
|
-
? Number.NaN
|
|
837
|
-
: workletGlobal.__frameTimestamp ||
|
|
838
|
-
(typeof workletGlobal._getAnimationTimestamp === 'function'
|
|
839
|
-
? workletGlobal._getAnimationTimestamp()
|
|
840
|
-
: Number.NaN);
|
|
841
|
-
let animationId: number;
|
|
842
|
-
|
|
843
|
-
if (animation.type === 'timing' && useV2) {
|
|
844
|
-
const [x1, y1, x2, y2] = animation.controlPoints;
|
|
845
|
-
animationId =
|
|
846
|
-
from !== undefined
|
|
847
|
-
? animateTimingFromV2HostFunction!(
|
|
848
|
-
driverId,
|
|
849
|
-
startClip.x,
|
|
850
|
-
startClip.y,
|
|
851
|
-
startClip.width,
|
|
852
|
-
startClip.height,
|
|
853
|
-
startClip.topLeftRadius,
|
|
854
|
-
startClip.topRightRadius,
|
|
855
|
-
startClip.bottomRightRadius,
|
|
856
|
-
startClip.bottomLeftRadius,
|
|
857
|
-
curveCode(dispatchStart),
|
|
858
|
-
dispatchStart.contentTranslateX,
|
|
859
|
-
dispatchStart.contentTranslateY,
|
|
860
|
-
dispatchStart.contentScale,
|
|
861
|
-
targetClip.x,
|
|
862
|
-
targetClip.y,
|
|
863
|
-
targetClip.width,
|
|
864
|
-
targetClip.height,
|
|
865
|
-
targetClip.topLeftRadius,
|
|
866
|
-
targetClip.topRightRadius,
|
|
867
|
-
targetClip.bottomRightRadius,
|
|
868
|
-
targetClip.bottomLeftRadius,
|
|
869
|
-
curveCode(canonicalTarget),
|
|
870
|
-
canonicalTarget.contentTranslateX,
|
|
871
|
-
canonicalTarget.contentTranslateY,
|
|
872
|
-
canonicalTarget.contentScale,
|
|
873
|
-
animation.duration,
|
|
874
|
-
x1,
|
|
875
|
-
y1,
|
|
876
|
-
x2,
|
|
877
|
-
y2,
|
|
878
|
-
reduceMotion,
|
|
879
|
-
startedAtMs
|
|
880
|
-
)
|
|
881
|
-
: animateTimingV2HostFunction!(
|
|
882
|
-
driverId,
|
|
883
|
-
hasInteractiveStart,
|
|
884
|
-
startClip.x,
|
|
885
|
-
startClip.y,
|
|
886
|
-
startClip.width,
|
|
887
|
-
startClip.height,
|
|
888
|
-
startClip.topLeftRadius,
|
|
889
|
-
startClip.topRightRadius,
|
|
890
|
-
startClip.bottomRightRadius,
|
|
891
|
-
startClip.bottomLeftRadius,
|
|
892
|
-
curveCode(dispatchStart),
|
|
893
|
-
dispatchStart.contentTranslateX,
|
|
894
|
-
dispatchStart.contentTranslateY,
|
|
895
|
-
dispatchStart.contentScale,
|
|
896
|
-
targetClip.x,
|
|
897
|
-
targetClip.y,
|
|
898
|
-
targetClip.width,
|
|
899
|
-
targetClip.height,
|
|
900
|
-
targetClip.topLeftRadius,
|
|
901
|
-
targetClip.topRightRadius,
|
|
902
|
-
targetClip.bottomRightRadius,
|
|
903
|
-
targetClip.bottomLeftRadius,
|
|
904
|
-
curveCode(canonicalTarget),
|
|
905
|
-
canonicalTarget.contentTranslateX,
|
|
906
|
-
canonicalTarget.contentTranslateY,
|
|
907
|
-
canonicalTarget.contentScale,
|
|
908
|
-
animation.duration,
|
|
909
|
-
x1,
|
|
910
|
-
y1,
|
|
911
|
-
x2,
|
|
912
|
-
y2,
|
|
913
|
-
reduceMotion,
|
|
914
|
-
startedAtMs
|
|
915
|
-
);
|
|
916
|
-
} else if (animation.type === 'timing') {
|
|
917
|
-
const [x1, y1, x2, y2] = animation.controlPoints;
|
|
918
|
-
animationId = animateTimingHostFunction(
|
|
919
|
-
driverId,
|
|
920
|
-
hasInteractiveStart,
|
|
921
|
-
startClip.x,
|
|
922
|
-
startClip.y,
|
|
923
|
-
startClip.width,
|
|
924
|
-
startClip.height,
|
|
925
|
-
startClip.radius,
|
|
926
|
-
start.contentTranslateX,
|
|
927
|
-
start.contentTranslateY,
|
|
928
|
-
targetClip.x,
|
|
929
|
-
targetClip.y,
|
|
930
|
-
targetClip.width,
|
|
931
|
-
targetClip.height,
|
|
932
|
-
targetClip.radius,
|
|
933
|
-
canonicalTarget.contentTranslateX,
|
|
934
|
-
canonicalTarget.contentTranslateY,
|
|
935
|
-
Math.max(0, animation.duration),
|
|
936
|
-
x1,
|
|
937
|
-
y1,
|
|
938
|
-
x2,
|
|
939
|
-
y2,
|
|
940
|
-
reduceMotion,
|
|
941
|
-
startedAtMs
|
|
942
|
-
);
|
|
943
|
-
} else if (animation.type === 'spring' && useV2) {
|
|
944
|
-
const inheritVelocity =
|
|
945
|
-
animation.initialVelocity === undefined ||
|
|
946
|
-
animation.initialVelocity === 'inherit';
|
|
947
|
-
const mass = animation.mass ?? 1;
|
|
948
|
-
const stiffness = animation.stiffness ?? 100;
|
|
949
|
-
const damping = animation.damping ?? 10;
|
|
950
|
-
const initialVelocity = inheritVelocity
|
|
951
|
-
? 0
|
|
952
|
-
: (animation.initialVelocity as number);
|
|
953
|
-
animationId =
|
|
954
|
-
from !== undefined
|
|
955
|
-
? animateSpringFromV2HostFunction!(
|
|
956
|
-
driverId,
|
|
957
|
-
startClip.x,
|
|
958
|
-
startClip.y,
|
|
959
|
-
startClip.width,
|
|
960
|
-
startClip.height,
|
|
961
|
-
startClip.topLeftRadius,
|
|
962
|
-
startClip.topRightRadius,
|
|
963
|
-
startClip.bottomRightRadius,
|
|
964
|
-
startClip.bottomLeftRadius,
|
|
965
|
-
curveCode(dispatchStart),
|
|
966
|
-
dispatchStart.contentTranslateX,
|
|
967
|
-
dispatchStart.contentTranslateY,
|
|
968
|
-
dispatchStart.contentScale,
|
|
969
|
-
targetClip.x,
|
|
970
|
-
targetClip.y,
|
|
971
|
-
targetClip.width,
|
|
972
|
-
targetClip.height,
|
|
973
|
-
targetClip.topLeftRadius,
|
|
974
|
-
targetClip.topRightRadius,
|
|
975
|
-
targetClip.bottomRightRadius,
|
|
976
|
-
targetClip.bottomLeftRadius,
|
|
977
|
-
curveCode(canonicalTarget),
|
|
978
|
-
canonicalTarget.contentTranslateX,
|
|
979
|
-
canonicalTarget.contentTranslateY,
|
|
980
|
-
canonicalTarget.contentScale,
|
|
981
|
-
mass,
|
|
982
|
-
stiffness,
|
|
983
|
-
damping,
|
|
984
|
-
initialVelocity,
|
|
985
|
-
inheritVelocity,
|
|
986
|
-
reduceMotion,
|
|
987
|
-
startedAtMs
|
|
988
|
-
)
|
|
989
|
-
: animateSpringV2HostFunction!(
|
|
990
|
-
driverId,
|
|
991
|
-
hasInteractiveStart,
|
|
992
|
-
startClip.x,
|
|
993
|
-
startClip.y,
|
|
994
|
-
startClip.width,
|
|
995
|
-
startClip.height,
|
|
996
|
-
startClip.topLeftRadius,
|
|
997
|
-
startClip.topRightRadius,
|
|
998
|
-
startClip.bottomRightRadius,
|
|
999
|
-
startClip.bottomLeftRadius,
|
|
1000
|
-
curveCode(dispatchStart),
|
|
1001
|
-
dispatchStart.contentTranslateX,
|
|
1002
|
-
dispatchStart.contentTranslateY,
|
|
1003
|
-
dispatchStart.contentScale,
|
|
1004
|
-
targetClip.x,
|
|
1005
|
-
targetClip.y,
|
|
1006
|
-
targetClip.width,
|
|
1007
|
-
targetClip.height,
|
|
1008
|
-
targetClip.topLeftRadius,
|
|
1009
|
-
targetClip.topRightRadius,
|
|
1010
|
-
targetClip.bottomRightRadius,
|
|
1011
|
-
targetClip.bottomLeftRadius,
|
|
1012
|
-
curveCode(canonicalTarget),
|
|
1013
|
-
canonicalTarget.contentTranslateX,
|
|
1014
|
-
canonicalTarget.contentTranslateY,
|
|
1015
|
-
canonicalTarget.contentScale,
|
|
1016
|
-
mass,
|
|
1017
|
-
stiffness,
|
|
1018
|
-
damping,
|
|
1019
|
-
initialVelocity,
|
|
1020
|
-
inheritVelocity,
|
|
1021
|
-
reduceMotion,
|
|
1022
|
-
startedAtMs
|
|
1023
|
-
);
|
|
1024
|
-
} else if (animation.type === 'spring') {
|
|
1025
|
-
const inheritVelocity =
|
|
1026
|
-
animation.initialVelocity === undefined ||
|
|
1027
|
-
animation.initialVelocity === 'inherit';
|
|
1028
|
-
animationId = animateSpringHostFunction(
|
|
1029
|
-
driverId,
|
|
1030
|
-
hasInteractiveStart,
|
|
1031
|
-
startClip.x,
|
|
1032
|
-
startClip.y,
|
|
1033
|
-
startClip.width,
|
|
1034
|
-
startClip.height,
|
|
1035
|
-
startClip.radius,
|
|
1036
|
-
start.contentTranslateX,
|
|
1037
|
-
start.contentTranslateY,
|
|
1038
|
-
targetClip.x,
|
|
1039
|
-
targetClip.y,
|
|
1040
|
-
targetClip.width,
|
|
1041
|
-
targetClip.height,
|
|
1042
|
-
targetClip.radius,
|
|
1043
|
-
canonicalTarget.contentTranslateX,
|
|
1044
|
-
canonicalTarget.contentTranslateY,
|
|
1045
|
-
Math.max(0.0001, animation.mass ?? 1),
|
|
1046
|
-
Math.max(0.0001, animation.stiffness ?? 100),
|
|
1047
|
-
Math.max(0, animation.damping ?? 10),
|
|
1048
|
-
inheritVelocity ? 0 : (animation.initialVelocity as number),
|
|
1049
|
-
inheritVelocity,
|
|
1050
|
-
reduceMotion,
|
|
1051
|
-
startedAtMs
|
|
1052
|
-
);
|
|
1053
|
-
} else if (useV2) {
|
|
1054
|
-
const frames = (keyframeResult as NonNullable<typeof keyframeResult>)
|
|
1055
|
-
.frames;
|
|
1056
|
-
animationId =
|
|
1057
|
-
from !== undefined
|
|
1058
|
-
? animateKeyframesFromV2HostFunction!(
|
|
1059
|
-
driverId,
|
|
1060
|
-
startClip.x,
|
|
1061
|
-
startClip.y,
|
|
1062
|
-
startClip.width,
|
|
1063
|
-
startClip.height,
|
|
1064
|
-
startClip.topLeftRadius,
|
|
1065
|
-
startClip.topRightRadius,
|
|
1066
|
-
startClip.bottomRightRadius,
|
|
1067
|
-
startClip.bottomLeftRadius,
|
|
1068
|
-
curveCode(dispatchStart),
|
|
1069
|
-
dispatchStart.contentTranslateX,
|
|
1070
|
-
dispatchStart.contentTranslateY,
|
|
1071
|
-
dispatchStart.contentScale,
|
|
1072
|
-
targetClip.x,
|
|
1073
|
-
targetClip.y,
|
|
1074
|
-
targetClip.width,
|
|
1075
|
-
targetClip.height,
|
|
1076
|
-
targetClip.topLeftRadius,
|
|
1077
|
-
targetClip.topRightRadius,
|
|
1078
|
-
targetClip.bottomRightRadius,
|
|
1079
|
-
targetClip.bottomLeftRadius,
|
|
1080
|
-
curveCode(canonicalTarget),
|
|
1081
|
-
canonicalTarget.contentTranslateX,
|
|
1082
|
-
canonicalTarget.contentTranslateY,
|
|
1083
|
-
canonicalTarget.contentScale,
|
|
1084
|
-
animation.duration,
|
|
1085
|
-
frames,
|
|
1086
|
-
reduceMotion,
|
|
1087
|
-
startedAtMs
|
|
1088
|
-
)
|
|
1089
|
-
: animateKeyframesV2HostFunction!(
|
|
1090
|
-
driverId,
|
|
1091
|
-
hasInteractiveStart,
|
|
1092
|
-
startClip.x,
|
|
1093
|
-
startClip.y,
|
|
1094
|
-
startClip.width,
|
|
1095
|
-
startClip.height,
|
|
1096
|
-
startClip.topLeftRadius,
|
|
1097
|
-
startClip.topRightRadius,
|
|
1098
|
-
startClip.bottomRightRadius,
|
|
1099
|
-
startClip.bottomLeftRadius,
|
|
1100
|
-
curveCode(dispatchStart),
|
|
1101
|
-
dispatchStart.contentTranslateX,
|
|
1102
|
-
dispatchStart.contentTranslateY,
|
|
1103
|
-
dispatchStart.contentScale,
|
|
1104
|
-
targetClip.x,
|
|
1105
|
-
targetClip.y,
|
|
1106
|
-
targetClip.width,
|
|
1107
|
-
targetClip.height,
|
|
1108
|
-
targetClip.topLeftRadius,
|
|
1109
|
-
targetClip.topRightRadius,
|
|
1110
|
-
targetClip.bottomRightRadius,
|
|
1111
|
-
targetClip.bottomLeftRadius,
|
|
1112
|
-
curveCode(canonicalTarget),
|
|
1113
|
-
canonicalTarget.contentTranslateX,
|
|
1114
|
-
canonicalTarget.contentTranslateY,
|
|
1115
|
-
canonicalTarget.contentScale,
|
|
1116
|
-
animation.duration,
|
|
1117
|
-
frames,
|
|
1118
|
-
reduceMotion,
|
|
1119
|
-
startedAtMs
|
|
1120
|
-
);
|
|
1121
|
-
} else {
|
|
1122
|
-
animationId = animateKeyframesHostFunction(
|
|
1123
|
-
driverId,
|
|
1124
|
-
hasInteractiveStart,
|
|
1125
|
-
startClip.x,
|
|
1126
|
-
startClip.y,
|
|
1127
|
-
startClip.width,
|
|
1128
|
-
startClip.height,
|
|
1129
|
-
startClip.radius,
|
|
1130
|
-
start.contentTranslateX,
|
|
1131
|
-
start.contentTranslateY,
|
|
1132
|
-
targetClip.x,
|
|
1133
|
-
targetClip.y,
|
|
1134
|
-
targetClip.width,
|
|
1135
|
-
targetClip.height,
|
|
1136
|
-
targetClip.radius,
|
|
1137
|
-
canonicalTarget.contentTranslateX,
|
|
1138
|
-
canonicalTarget.contentTranslateY,
|
|
1139
|
-
Math.max(0, animation.duration),
|
|
1140
|
-
// Non-null here: the validation gate above rejected null before any
|
|
1141
|
-
// side effect.
|
|
1142
|
-
flattenV1Keyframes(
|
|
1143
|
-
(keyframeResult as NonNullable<typeof keyframeResult>)
|
|
1144
|
-
.canonicalFrames,
|
|
1145
|
-
animation.frames.map((frame) => frame.offset)
|
|
1146
|
-
),
|
|
1147
|
-
reduceMotion,
|
|
1148
|
-
startedAtMs
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
if (animationId === 0) {
|
|
1152
|
-
// The 0 sentinel: off-main, invalid-id, or otherwise unsupported
|
|
1153
|
-
// dispatch. A missing driver is accepted when this call carries the
|
|
1154
|
-
// authoritative interactive start above; a start-less missing-state
|
|
1155
|
-
// request remains unsupported rather than inventing zero geometry.
|
|
1156
|
-
// Validation failures never get here: they reject before any side
|
|
1157
|
-
// effect, with a fresh id and one finished:false completion once the
|
|
1158
|
-
// native entry exists — in the pre-seed window rejectAnimation has no
|
|
1159
|
-
// entry and returns the bare 0 sentinel with no completion instead.
|
|
1160
|
-
if (!useV2) {
|
|
1161
|
-
ownership.value = INTERACTIVE;
|
|
1162
|
-
presentation.value = start;
|
|
1163
|
-
if (scalarsWereStale) scalarsStale.value = 1;
|
|
1164
|
-
activeAnimationId.value = 0;
|
|
1165
|
-
}
|
|
1166
|
-
return rejectAnimationHostFunction(driverId);
|
|
1167
|
-
}
|
|
1168
|
-
if (useV2) {
|
|
1169
|
-
ownership.value = NATIVE;
|
|
1170
|
-
presentation.value = canonicalTarget;
|
|
1171
|
-
scalarsStale.value = 0;
|
|
1172
|
-
}
|
|
1173
|
-
activeAnimationId.value = animationId;
|
|
1174
|
-
return animationId;
|
|
1175
|
-
};
|
|
1176
|
-
|
|
1177
|
-
const cancelOnUI = (
|
|
1178
|
-
animationId = 0,
|
|
1179
|
-
behavior: 'current' | 'target' = 'current'
|
|
1180
|
-
): CanonicalSmoothClipPresentation => {
|
|
1181
|
-
'worklet';
|
|
1182
|
-
// Past the hook's cleanup the native entry is gone; the call would fail
|
|
1183
|
-
// defined (handled=false) but it is the last driver.ui path that still
|
|
1184
|
-
// crosses into native after the tombstone. Guard it like the rest.
|
|
1185
|
-
const fallback =
|
|
1186
|
-
canonicalizeClipPresentation(presentation.value) ?? initialPresentation;
|
|
1187
|
-
if (disposed.value !== 0) return fallback;
|
|
1188
|
-
const values =
|
|
1189
|
-
PRESENTATION_PROTOCOL_VERSION === 2 && cancelAnimationV2HostFunction
|
|
1190
|
-
? cancelAnimationV2HostFunction(
|
|
1191
|
-
driverId,
|
|
1192
|
-
animationId,
|
|
1193
|
-
behavior === 'target' ? 1 : 0
|
|
1194
|
-
)
|
|
1195
|
-
: cancelAnimationHostFunction(
|
|
1196
|
-
driverId,
|
|
1197
|
-
animationId,
|
|
1198
|
-
behavior === 'target' ? 1 : 0
|
|
1199
|
-
);
|
|
1200
|
-
if (values.length < 8 || values[0] !== 1) return fallback;
|
|
1201
|
-
const current = presentationFromNative(values, fallback, 1);
|
|
1202
|
-
activeAnimationId.value = 0;
|
|
1203
|
-
ownership.value = INTERACTIVE;
|
|
1204
|
-
scalarsStale.value = 0;
|
|
1205
|
-
seedPresentation(current);
|
|
1206
|
-
return current;
|
|
1207
|
-
};
|
|
1208
|
-
|
|
1209
|
-
const driver: SmoothClipDriver = {
|
|
1210
|
-
kind: 'hybrid',
|
|
1211
|
-
presentation,
|
|
1212
|
-
ui: {
|
|
1213
|
-
beginInteraction() {
|
|
1214
|
-
'worklet';
|
|
1215
|
-
if (isRNRuntime()) return uiOnly();
|
|
1216
|
-
return beginOnUI();
|
|
1217
|
-
},
|
|
1218
|
-
set(next) {
|
|
1219
|
-
'worklet';
|
|
1220
|
-
if (isRNRuntime()) return uiOnly();
|
|
1221
|
-
setOnUI(next);
|
|
1222
|
-
},
|
|
1223
|
-
setScalars(
|
|
1224
|
-
x,
|
|
1225
|
-
y,
|
|
1226
|
-
width,
|
|
1227
|
-
height,
|
|
1228
|
-
radius,
|
|
1229
|
-
contentTranslateX,
|
|
1230
|
-
contentTranslateY
|
|
1231
|
-
) {
|
|
1232
|
-
'worklet';
|
|
1233
|
-
if (isRNRuntime()) return uiOnly();
|
|
1234
|
-
setScalarsOnUI(
|
|
1235
|
-
x,
|
|
1236
|
-
y,
|
|
1237
|
-
width,
|
|
1238
|
-
height,
|
|
1239
|
-
radius,
|
|
1240
|
-
contentTranslateX,
|
|
1241
|
-
contentTranslateY
|
|
1242
|
-
);
|
|
1243
|
-
},
|
|
1244
|
-
setPresentationScalars(
|
|
1245
|
-
x,
|
|
1246
|
-
y,
|
|
1247
|
-
width,
|
|
1248
|
-
height,
|
|
1249
|
-
topLeftRadius,
|
|
1250
|
-
topRightRadius,
|
|
1251
|
-
bottomRightRadius,
|
|
1252
|
-
bottomLeftRadius,
|
|
1253
|
-
nextCurveCode,
|
|
1254
|
-
contentTranslateX,
|
|
1255
|
-
contentTranslateY,
|
|
1256
|
-
contentScale
|
|
1257
|
-
) {
|
|
1258
|
-
'worklet';
|
|
1259
|
-
if (isRNRuntime()) return uiOnly();
|
|
1260
|
-
setPresentationScalarsOnUI(
|
|
1261
|
-
x,
|
|
1262
|
-
y,
|
|
1263
|
-
width,
|
|
1264
|
-
height,
|
|
1265
|
-
topLeftRadius,
|
|
1266
|
-
topRightRadius,
|
|
1267
|
-
bottomRightRadius,
|
|
1268
|
-
bottomLeftRadius,
|
|
1269
|
-
nextCurveCode,
|
|
1270
|
-
contentTranslateX,
|
|
1271
|
-
contentTranslateY,
|
|
1272
|
-
contentScale
|
|
1273
|
-
);
|
|
1274
|
-
},
|
|
1275
|
-
animateTo(target, animation) {
|
|
1276
|
-
'worklet';
|
|
1277
|
-
if (isRNRuntime()) return uiOnly();
|
|
1278
|
-
return animateOnUI(target, animation);
|
|
1279
|
-
},
|
|
1280
|
-
cancel(animationId = 0, behavior = 'current') {
|
|
1281
|
-
'worklet';
|
|
1282
|
-
if (isRNRuntime()) return uiOnly();
|
|
1283
|
-
return cancelOnUI(animationId, behavior);
|
|
1284
|
-
},
|
|
1285
|
-
},
|
|
1286
|
-
react: {
|
|
1287
|
-
beginInteraction() {
|
|
1288
|
-
const { requestId, promise } =
|
|
1289
|
-
createReactRequest<CanonicalSmoothClipPresentation>(driverId);
|
|
1290
|
-
scheduleOnUI(() => {
|
|
1291
|
-
'worklet';
|
|
1292
|
-
const result = beginOnUI();
|
|
1293
|
-
scheduleOnRN(resolveReactRequest, driverId, requestId, result);
|
|
1294
|
-
});
|
|
1295
|
-
return promise;
|
|
1296
|
-
},
|
|
1297
|
-
set(next) {
|
|
1298
|
-
// Teardown resolves (undefined) rather than rejects: `set` is a
|
|
1299
|
-
// documented fire-and-forget call, and a promise discarded with
|
|
1300
|
-
// `void` must not become an unhandled rejection at unmount.
|
|
1301
|
-
const { requestId, promise } = createReactRequest<void>(
|
|
1302
|
-
driverId,
|
|
1303
|
-
false,
|
|
1304
|
-
{ value: undefined }
|
|
1305
|
-
);
|
|
1306
|
-
scheduleOnUI(() => {
|
|
1307
|
-
'worklet';
|
|
1308
|
-
setOnUI(next);
|
|
1309
|
-
scheduleOnRN(resolveReactRequest, driverId, requestId, undefined);
|
|
1310
|
-
});
|
|
1311
|
-
return promise;
|
|
1312
|
-
},
|
|
1313
|
-
animateTo(target, animation) {
|
|
1314
|
-
// Teardown resolves with the documented 0 rejection sentinel (see
|
|
1315
|
-
// `set` above): the README's own example voids this promise.
|
|
1316
|
-
const { requestId, promise } = createReactRequest<number>(
|
|
1317
|
-
driverId,
|
|
1318
|
-
true,
|
|
1319
|
-
{ value: 0 }
|
|
1320
|
-
);
|
|
1321
|
-
scheduleOnUI(() => {
|
|
1322
|
-
'worklet';
|
|
1323
|
-
const result = animateOnUI(target, animation);
|
|
1324
|
-
scheduleOnRN(
|
|
1325
|
-
resolveReactRequest,
|
|
1326
|
-
driverId,
|
|
1327
|
-
requestId,
|
|
1328
|
-
result,
|
|
1329
|
-
true
|
|
1330
|
-
);
|
|
1331
|
-
});
|
|
1332
|
-
return promise;
|
|
1333
|
-
},
|
|
1334
|
-
cancel(animationId = 0, behavior = 'current') {
|
|
1335
|
-
const { requestId, promise } =
|
|
1336
|
-
createReactRequest<CanonicalSmoothClipPresentation>(driverId, true);
|
|
1337
|
-
scheduleOnUI(() => {
|
|
1338
|
-
'worklet';
|
|
1339
|
-
const result = cancelOnUI(animationId, behavior);
|
|
1340
|
-
scheduleOnRN(
|
|
1341
|
-
resolveReactRequest,
|
|
1342
|
-
driverId,
|
|
1343
|
-
requestId,
|
|
1344
|
-
result,
|
|
1345
|
-
true
|
|
1346
|
-
);
|
|
1347
|
-
});
|
|
1348
|
-
return promise;
|
|
1349
|
-
},
|
|
1350
|
-
},
|
|
1351
|
-
__smoothClipHandle: {
|
|
1352
|
-
driverId,
|
|
1353
|
-
presentation,
|
|
1354
|
-
ownership,
|
|
1355
|
-
activeAnimationId,
|
|
1356
|
-
disposed,
|
|
1357
|
-
},
|
|
1358
|
-
};
|
|
1359
|
-
|
|
1360
|
-
setDriverState(
|
|
1361
|
-
driver,
|
|
1362
|
-
createDriverState(
|
|
1363
|
-
driverId,
|
|
1364
|
-
initialPresentation,
|
|
1365
|
-
presentation,
|
|
1366
|
-
callbackRef,
|
|
1367
|
-
activeAnimationId,
|
|
1368
|
-
ownership
|
|
1369
|
-
)
|
|
1370
|
-
);
|
|
1371
|
-
driverRef.current = driver;
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
const driver = driverRef.current;
|
|
1375
|
-
|
|
1376
|
-
useEffect(() => {
|
|
1377
|
-
const currentDriver = driverRef.current;
|
|
1378
|
-
if (!currentDriver) return undefined;
|
|
1379
|
-
const state = getDriverState(currentDriver);
|
|
1380
|
-
const { driverId } = state;
|
|
1381
|
-
// Re-attach on every effect run: StrictMode/<Activity> replay the effect
|
|
1382
|
-
// after its cleanup detached this state and destroyed the native driver.
|
|
1383
|
-
// The authoritative seed below recreates the native entry in that case.
|
|
1384
|
-
attachDriverState(state);
|
|
1385
|
-
|
|
1386
|
-
const subscription = NativeSmoothClipModule.onClipAnimationComplete(
|
|
1387
|
-
(result) => {
|
|
1388
|
-
if (result.driverId !== driverId) return;
|
|
1389
|
-
synchronizeNativeCompletion(
|
|
1390
|
-
activeAnimationId,
|
|
1391
|
-
ownership,
|
|
1392
|
-
result.animationId,
|
|
1393
|
-
result.finished
|
|
1394
|
-
);
|
|
1395
|
-
completeNativeAnimation(driverId, result.animationId, result.finished);
|
|
1396
|
-
}
|
|
1397
|
-
);
|
|
1398
|
-
|
|
1399
|
-
scheduleOnUI(
|
|
1400
|
-
(
|
|
1401
|
-
source: SharedValue<SmoothClipPresentation>,
|
|
1402
|
-
listenerId: number,
|
|
1403
|
-
nativeDriverId: number,
|
|
1404
|
-
owner: SharedValue<number>,
|
|
1405
|
-
suppressed: SharedValue<number>,
|
|
1406
|
-
active: SharedValue<number>,
|
|
1407
|
-
stale: SharedValue<number>,
|
|
1408
|
-
gone: SharedValue<number>,
|
|
1409
|
-
setter: typeof setPresentationHostFunction,
|
|
1410
|
-
setterV2: typeof setPresentationV2HostFunction,
|
|
1411
|
-
protocolVersion: number
|
|
1412
|
-
) => {
|
|
1413
|
-
'worklet';
|
|
1414
|
-
// First, before the listener or the seed: this effect run owns the
|
|
1415
|
-
// driver again, so a StrictMode/<Activity> replay must clear the
|
|
1416
|
-
// previous cleanup's tombstone or every later write would be rejected.
|
|
1417
|
-
gone.value = 0;
|
|
1418
|
-
let last: SmoothClipPresentation | null = null;
|
|
1419
|
-
const deliver = (next: SmoothClipPresentation) => {
|
|
1420
|
-
const canonical = canonicalizeClipPresentation(next);
|
|
1421
|
-
if (canonical === null || clipPresentationEquals(last, canonical)) {
|
|
1422
|
-
return;
|
|
1423
|
-
}
|
|
1424
|
-
// Record `last` only for values native has actually observed:
|
|
1425
|
-
// deliveries dropped during native ownership must stay eligible
|
|
1426
|
-
// for re-delivery once interactive ownership returns.
|
|
1427
|
-
if (owner.value !== INTERACTIVE) return;
|
|
1428
|
-
if (suppressed.value > 0) {
|
|
1429
|
-
suppressed.value -= 1;
|
|
1430
|
-
last = canonical;
|
|
1431
|
-
return;
|
|
1432
|
-
}
|
|
1433
|
-
if (protocolVersion !== 2 && !isV1Compatible(canonical)) {
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
last = canonical;
|
|
1437
|
-
if (stale.value !== 0) stale.value = 0;
|
|
1438
|
-
const { clip, contentTranslateX, contentTranslateY, contentScale } =
|
|
1439
|
-
canonical;
|
|
1440
|
-
// Declarative deliveries always record velocity samples — only the
|
|
1441
|
-
// setScalars hot path is gated by the velocityTracking option.
|
|
1442
|
-
if (protocolVersion === 2 && setterV2) {
|
|
1443
|
-
setterV2(
|
|
1444
|
-
nativeDriverId,
|
|
1445
|
-
clip.x,
|
|
1446
|
-
clip.y,
|
|
1447
|
-
clip.width,
|
|
1448
|
-
clip.height,
|
|
1449
|
-
clip.topLeftRadius,
|
|
1450
|
-
clip.topRightRadius,
|
|
1451
|
-
clip.bottomRightRadius,
|
|
1452
|
-
clip.bottomLeftRadius,
|
|
1453
|
-
curveCode(canonical),
|
|
1454
|
-
contentTranslateX,
|
|
1455
|
-
contentTranslateY,
|
|
1456
|
-
contentScale,
|
|
1457
|
-
false,
|
|
1458
|
-
false,
|
|
1459
|
-
true
|
|
1460
|
-
);
|
|
1461
|
-
} else {
|
|
1462
|
-
setter(
|
|
1463
|
-
nativeDriverId,
|
|
1464
|
-
clip.x,
|
|
1465
|
-
clip.y,
|
|
1466
|
-
clip.width,
|
|
1467
|
-
clip.height,
|
|
1468
|
-
clip.radius,
|
|
1469
|
-
contentTranslateX,
|
|
1470
|
-
contentTranslateY,
|
|
1471
|
-
false,
|
|
1472
|
-
false
|
|
1473
|
-
);
|
|
1474
|
-
}
|
|
1475
|
-
};
|
|
1476
|
-
source.addListener(listenerId, deliver);
|
|
1477
|
-
// Authoritative take-ownership seed. Creates the native entry before
|
|
1478
|
-
// any view mounts on the ordinary path, and revives a driver destroyed
|
|
1479
|
-
// by an effect-replay cleanup (StrictMode/<Activity>) on later runs.
|
|
1480
|
-
// An animation worklet can win the scheduling race and create a latch
|
|
1481
|
-
// first; Native ownership then marks that intent as newer, so this
|
|
1482
|
-
// passive seed must not clear its active id or replay the target value.
|
|
1483
|
-
const current = canonicalizeClipPresentation(source.value);
|
|
1484
|
-
if (owner.value === INTERACTIVE && current !== null) {
|
|
1485
|
-
active.value = 0;
|
|
1486
|
-
owner.value = INTERACTIVE;
|
|
1487
|
-
stale.value = 0;
|
|
1488
|
-
last = current;
|
|
1489
|
-
if (protocolVersion === 2 && setterV2) {
|
|
1490
|
-
setterV2(
|
|
1491
|
-
nativeDriverId,
|
|
1492
|
-
current.clip.x,
|
|
1493
|
-
current.clip.y,
|
|
1494
|
-
current.clip.width,
|
|
1495
|
-
current.clip.height,
|
|
1496
|
-
current.clip.topLeftRadius,
|
|
1497
|
-
current.clip.topRightRadius,
|
|
1498
|
-
current.clip.bottomRightRadius,
|
|
1499
|
-
current.clip.bottomLeftRadius,
|
|
1500
|
-
curveCode(current),
|
|
1501
|
-
current.contentTranslateX,
|
|
1502
|
-
current.contentTranslateY,
|
|
1503
|
-
current.contentScale,
|
|
1504
|
-
true,
|
|
1505
|
-
false,
|
|
1506
|
-
true
|
|
1507
|
-
);
|
|
1508
|
-
} else if (isV1Compatible(current)) {
|
|
1509
|
-
setter(
|
|
1510
|
-
nativeDriverId,
|
|
1511
|
-
current.clip.x,
|
|
1512
|
-
current.clip.y,
|
|
1513
|
-
current.clip.width,
|
|
1514
|
-
current.clip.height,
|
|
1515
|
-
current.clip.radius,
|
|
1516
|
-
current.contentTranslateX,
|
|
1517
|
-
current.contentTranslateY,
|
|
1518
|
-
true,
|
|
1519
|
-
false
|
|
1520
|
-
);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
},
|
|
1524
|
-
presentation,
|
|
1525
|
-
driverId,
|
|
1526
|
-
driverId,
|
|
1527
|
-
ownership,
|
|
1528
|
-
suppressDeliveries,
|
|
1529
|
-
activeAnimationId,
|
|
1530
|
-
scalarsStale,
|
|
1531
|
-
disposed,
|
|
1532
|
-
setPresentationHostFunction,
|
|
1533
|
-
setPresentationV2HostFunction,
|
|
1534
|
-
PRESENTATION_PROTOCOL_VERSION
|
|
1535
|
-
);
|
|
1536
|
-
|
|
1537
|
-
return () => {
|
|
1538
|
-
subscription.remove();
|
|
1539
|
-
rejectDriverRequests(driverId);
|
|
1540
|
-
detachDriverState(state);
|
|
1541
|
-
scheduleOnUI(
|
|
1542
|
-
(
|
|
1543
|
-
source: SharedValue<SmoothClipPresentation>,
|
|
1544
|
-
listenerId: number,
|
|
1545
|
-
nativeDriverId: number,
|
|
1546
|
-
owner: SharedValue<number>,
|
|
1547
|
-
active: SharedValue<number>,
|
|
1548
|
-
stale: SharedValue<number>,
|
|
1549
|
-
suppressed: SharedValue<number>,
|
|
1550
|
-
gone: SharedValue<number>,
|
|
1551
|
-
destroy: typeof destroyDriverHostFunction
|
|
1552
|
-
) => {
|
|
1553
|
-
'worklet';
|
|
1554
|
-
source.removeListener(listenerId);
|
|
1555
|
-
destroy(nativeDriverId);
|
|
1556
|
-
// StrictMode replays the effect with these same SharedValues. Reset
|
|
1557
|
-
// the UI-side tombstone only after native teardown so the next setup
|
|
1558
|
-
// can seed or animate without inheriting stale Native ownership.
|
|
1559
|
-
active.value = 0;
|
|
1560
|
-
owner.value = INTERACTIVE;
|
|
1561
|
-
stale.value = 0;
|
|
1562
|
-
// Defensive: seedPresentation now reclaims its own unspent credit at
|
|
1563
|
-
// the point of issue, so this should already be 0. Kept because the
|
|
1564
|
-
// failure mode — one declarative write silently dropped for the rest
|
|
1565
|
-
// of the next hook's life — is invisible until someone reports a
|
|
1566
|
-
// stale clip rect.
|
|
1567
|
-
suppressed.value = 0;
|
|
1568
|
-
// Last: the native entry is now erased, so until the next effect run
|
|
1569
|
-
// clears this, any call that would recreate it is rejected instead.
|
|
1570
|
-
gone.value = 1;
|
|
1571
|
-
},
|
|
1572
|
-
presentation,
|
|
1573
|
-
driverId,
|
|
1574
|
-
driverId,
|
|
1575
|
-
ownership,
|
|
1576
|
-
activeAnimationId,
|
|
1577
|
-
scalarsStale,
|
|
1578
|
-
suppressDeliveries,
|
|
1579
|
-
disposed,
|
|
1580
|
-
destroyDriverHostFunction
|
|
1581
|
-
);
|
|
1582
|
-
};
|
|
1583
|
-
}, [
|
|
1584
|
-
activeAnimationId,
|
|
1585
|
-
disposed,
|
|
1586
|
-
ownership,
|
|
1587
|
-
presentation,
|
|
1588
|
-
scalarsStale,
|
|
1589
|
-
suppressDeliveries,
|
|
1590
|
-
]);
|
|
1591
|
-
|
|
1592
|
-
return driver;
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
export type {
|
|
1596
|
-
KeyframedClipAnimation,
|
|
1597
|
-
SpringClipAnimation,
|
|
1598
|
-
TimingClipAnimation,
|
|
1599
|
-
};
|