@rootnative/impulse 0.0.0-alpha.0 → 0.0.0-alpha.1
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/CHANGELOG.md +51 -5
- package/README.md +206 -11
- package/dist/{chunk-5BMRKYVY.js → chunk-2UZTAWUQ.js} +20 -2
- package/dist/chunk-BNSFDNLA.js +75 -0
- package/dist/chunk-DXXGWG4Q.js +136 -0
- package/dist/{chunk-PMR25UCT.js → chunk-HGBCIL6X.js} +1 -1
- package/dist/chunk-HI5PHDJY.js +94 -0
- package/dist/{chunk-IG5RXCYR.js → chunk-IH7SQ5X6.js} +11 -15
- package/dist/chunk-MWCIEVTA.js +199 -0
- package/dist/{chunk-F4RHM4ZK.js → chunk-NYDDZD4G.js} +58 -1
- package/dist/chunk-PGOQSKEJ.js +144 -0
- package/dist/{chunk-FR242SUF.js → chunk-TGOGIZDH.js} +13 -7
- package/dist/chunk-VEPUHGPN.js +12 -0
- package/dist/chunk-YZHAQ4XK.js +136 -0
- package/dist/compose/index.d.ts +1 -1
- package/dist/double-tap/index.d.ts +184 -0
- package/dist/double-tap/index.js +5 -0
- package/dist/drag/index.d.ts +18 -13
- package/dist/drag/index.js +3 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +12 -5
- package/dist/long-press/index.d.ts +219 -0
- package/dist/long-press/index.js +4 -0
- package/dist/pan/index.d.ts +221 -0
- package/dist/pan/index.js +4 -0
- package/dist/pinch/index.d.ts +239 -0
- package/dist/pinch/index.js +4 -0
- package/dist/raw/index.d.ts +3 -3
- package/dist/raw/index.js +2 -2
- package/dist/rotate/index.d.ts +263 -0
- package/dist/rotate/index.js +4 -0
- package/dist/swipe/index.d.ts +254 -0
- package/dist/swipe/index.js +4 -0
- package/dist/tap/index.d.ts +34 -29
- package/dist/tap/index.js +4 -3
- package/dist/tapEvent-KSSojt_l.d.ts +28 -0
- package/dist/{types-Ch2HM3aP.d.ts → types-ChGKY28a.d.ts} +27 -1
- package/dist/{useGestureMemo-Ccv8rB0C.d.ts → useGestureMemo-BRW1EKcJ.d.ts} +1 -1
- package/jest-setup.cjs +32 -1
- package/llms.txt +155 -0
- package/package.json +35 -2
- package/src/index.ts +50 -4
- package/src/intents/double-tap/index.ts +6 -0
- package/src/intents/long-press/index.ts +6 -0
- package/src/intents/pan/index.ts +2 -0
- package/src/intents/pinch/index.ts +2 -0
- package/src/intents/rotate/index.ts +6 -0
- package/src/intents/swipe/index.ts +8 -0
- package/src/intents/tapEvent.ts +50 -0
- package/src/intents/useDoubleTap.ts +321 -0
- package/src/intents/useDrag.ts +49 -28
- package/src/intents/useLongPress.ts +391 -0
- package/src/intents/usePan.ts +444 -0
- package/src/intents/usePinch.ts +483 -0
- package/src/intents/useRotate.ts +507 -0
- package/src/intents/useSwipe.ts +585 -0
- package/src/intents/useTap.ts +51 -60
- package/src/internal/intentResult.ts +67 -0
- package/src/internal/phaseCallbacks.ts +51 -0
- package/src/internal/useGestureMemo.ts +32 -4
- package/src/internal/useLatestCallback.ts +2 -2
- package/src/raw/useRawGesture.ts +1 -1
- package/src/relations/index.ts +94 -0
- package/src/types.ts +27 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { PanGesture } from 'react-native-gesture-handler';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
4
|
+
import { P as Point, H as HitSlop, I as IntentEndInfo, c as IntentResult } from '../types-ChGKY28a.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/** Which way the pan is allowed to move. */
|
|
8
|
+
type PanAxis = 'x' | 'y' | 'both';
|
|
9
|
+
/** The intent-shaped payload a {@link usePan} callback receives. */
|
|
10
|
+
interface PanEvent {
|
|
11
|
+
/**
|
|
12
|
+
* How far the finger has moved since the pan activated.
|
|
13
|
+
*
|
|
14
|
+
* Measured from the activation point, not from touch-down, so it does not
|
|
15
|
+
* include the `threshold` travel the finger spent before the pan existed.
|
|
16
|
+
* It resets to zero at the start of every gesture — a pan reports movement
|
|
17
|
+
* and owns no position.
|
|
18
|
+
*/
|
|
19
|
+
readonly translation: Point;
|
|
20
|
+
/**
|
|
21
|
+
* How far the finger moved since the previous frame.
|
|
22
|
+
*
|
|
23
|
+
* The field `useDrag` cannot give, and the reason to reach for this hook:
|
|
24
|
+
* a value the consumer owns is advanced by adding this, rather than by
|
|
25
|
+
* re-deriving it from a translation Impulse already clamped.
|
|
26
|
+
*
|
|
27
|
+
* Zero outside `onUpdate`. The first frame after activation reports the
|
|
28
|
+
* movement since activation, so a value advanced by `change` never jumps
|
|
29
|
+
* by `threshold`.
|
|
30
|
+
*/
|
|
31
|
+
readonly change: Point;
|
|
32
|
+
/** Finger speed, in points per second. */
|
|
33
|
+
readonly velocity: Point;
|
|
34
|
+
/** The touch point relative to the window. */
|
|
35
|
+
readonly absolute: Point;
|
|
36
|
+
/** How many fingers are down. */
|
|
37
|
+
readonly pointers: number;
|
|
38
|
+
}
|
|
39
|
+
/** Options for {@link usePan}. */
|
|
40
|
+
interface UsePanOptions extends GestureMemoOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Which way the pan may move. Default `'both'`.
|
|
43
|
+
*
|
|
44
|
+
* The locked axis reports zero for the life of the hook — `axis: 'x'`
|
|
45
|
+
* leaves `y`, `translation.y`, and `change.y` at zero. The axis also
|
|
46
|
+
* decides the activation criterion, which is the part that matters inside
|
|
47
|
+
* a scroll view: see `threshold`.
|
|
48
|
+
*/
|
|
49
|
+
axis?: PanAxis;
|
|
50
|
+
/**
|
|
51
|
+
* How far the finger must travel before the pan activates, in points.
|
|
52
|
+
* Default `10`.
|
|
53
|
+
*
|
|
54
|
+
* On a single axis this is a directional threshold, so a pan with
|
|
55
|
+
* `axis: 'x'` ignores vertical movement entirely and a vertical scroll view
|
|
56
|
+
* under it keeps working. On `'both'` it is a radial distance.
|
|
57
|
+
*/
|
|
58
|
+
threshold?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Movement across the axis that makes the pan fail, in points. Unset by
|
|
61
|
+
* default, which leaves RNGH's own behaviour in place.
|
|
62
|
+
*
|
|
63
|
+
* `threshold` decides when the pan wins; this decides when it gives up.
|
|
64
|
+
* Ignored when `axis` is `'both'`, which has no cross axis.
|
|
65
|
+
*/
|
|
66
|
+
failOffset?: number;
|
|
67
|
+
/**
|
|
68
|
+
* How many fingers must be on the view. Unset by default, which leaves
|
|
69
|
+
* RNGH's range of one to ten in place.
|
|
70
|
+
*
|
|
71
|
+
* Setting it fixes the count exactly, so `pointers: 2` is a two-finger pan
|
|
72
|
+
* that neither starts with one finger nor survives a third.
|
|
73
|
+
*/
|
|
74
|
+
pointers?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Extra touchable area around the view, in points.
|
|
77
|
+
*
|
|
78
|
+
* Written inline as an object is fine — the gesture is not rebuilt when the
|
|
79
|
+
* contents are unchanged.
|
|
80
|
+
*/
|
|
81
|
+
hitSlop?: HitSlop;
|
|
82
|
+
/**
|
|
83
|
+
* Whether the gesture is recognized at all. Default `true`.
|
|
84
|
+
*
|
|
85
|
+
* Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
|
|
86
|
+
* keeps its identity and its relations, so re-enabling it does not
|
|
87
|
+
* re-attach anything.
|
|
88
|
+
*/
|
|
89
|
+
enabled?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* The pan passed `threshold` and now owns the touch. **Runs on the JS
|
|
92
|
+
* thread** — Impulse owns the `scheduleOnRN` boundary, so this is an
|
|
93
|
+
* ordinary function and may touch React state.
|
|
94
|
+
*
|
|
95
|
+
* This is the first moment the pan has definitely won. `onBegin` fires
|
|
96
|
+
* earlier and promises nothing.
|
|
97
|
+
*/
|
|
98
|
+
onPanStart?: (event: PanEvent) => void;
|
|
99
|
+
/**
|
|
100
|
+
* The pan is over. **Runs on the JS thread.**
|
|
101
|
+
*
|
|
102
|
+
* Fires only for a pan that activated, so a touch that never passed the
|
|
103
|
+
* threshold never reaches here on either path.
|
|
104
|
+
*
|
|
105
|
+
* It fires for both endings, and `cancelled` says which. `false` is the
|
|
106
|
+
* finger lifting, and `velocity` then describes the release. `true` is the
|
|
107
|
+
* system taking the pan away — a competing gesture won, or the app went to
|
|
108
|
+
* the background. **There was no release on that path, so the velocity
|
|
109
|
+
* describes the last movement rather than a throw.**
|
|
110
|
+
*/
|
|
111
|
+
onPanEnd?: (event: PanEvent, info: IntentEndInfo) => void;
|
|
112
|
+
/**
|
|
113
|
+
* The finger went down and the gesture is now a candidate. **This is a
|
|
114
|
+
* worklet** — mark it with the `'worklet'` directive, and do not touch
|
|
115
|
+
* React state from it.
|
|
116
|
+
*
|
|
117
|
+
* Being a candidate is not the same as winning: the pan has not passed
|
|
118
|
+
* `threshold` yet and may never. Undo whatever it sets in `onFinalize`.
|
|
119
|
+
*/
|
|
120
|
+
onBegin?: (event: PanEvent) => void;
|
|
121
|
+
/**
|
|
122
|
+
* The pan moved. **This is a worklet**, and it runs on every frame the
|
|
123
|
+
* finger moves.
|
|
124
|
+
*
|
|
125
|
+
* This is where `change` is read, and it is the reason the hook exists:
|
|
126
|
+
* advance the value the consumer owns from here, on the thread that already
|
|
127
|
+
* holds it.
|
|
128
|
+
*
|
|
129
|
+
* There is no JS-thread counterpart on purpose. A per-frame `scheduleOnRN`
|
|
130
|
+
* is a scheduling cost paid sixty times a second for a value that is
|
|
131
|
+
* already on the thread that needs it.
|
|
132
|
+
*/
|
|
133
|
+
onUpdate?: (event: PanEvent) => void;
|
|
134
|
+
/**
|
|
135
|
+
* The gesture is over, whether it activated or not. **This is a worklet.**
|
|
136
|
+
*
|
|
137
|
+
* `success` is `true` when the pan activated and ended normally. This is
|
|
138
|
+
* the right place to clear anything `onBegin` set, because it runs on both
|
|
139
|
+
* paths.
|
|
140
|
+
*/
|
|
141
|
+
onFinalize?: (event: PanEvent, success: boolean) => void;
|
|
142
|
+
}
|
|
143
|
+
/** What {@link usePan} returns. */
|
|
144
|
+
interface UsePanResult extends IntentResult<PanGesture> {
|
|
145
|
+
/**
|
|
146
|
+
* How far the finger has moved along the x axis since the pan activated.
|
|
147
|
+
*
|
|
148
|
+
* **This is movement, not a position.** It is set to zero at the start of
|
|
149
|
+
* every gesture, so a second pan does not continue from where the first
|
|
150
|
+
* stopped. `useDrag` is the hook whose value accumulates.
|
|
151
|
+
*
|
|
152
|
+
* It keeps its final number after the gesture ends, so a release animation
|
|
153
|
+
* has something to animate from.
|
|
154
|
+
*
|
|
155
|
+
* Frozen at zero when `axis` is `'y'`.
|
|
156
|
+
*/
|
|
157
|
+
readonly x: SharedValue<number>;
|
|
158
|
+
/**
|
|
159
|
+
* How far the finger has moved along the y axis since the pan activated.
|
|
160
|
+
* Same terms as `x`. Frozen at zero when `axis` is `'x'`.
|
|
161
|
+
*/
|
|
162
|
+
readonly y: SharedValue<number>;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Recognize a pan, and report how the finger moved.
|
|
166
|
+
*
|
|
167
|
+
* ```tsx
|
|
168
|
+
* const camera = { x: useSharedValue(0), y: useSharedValue(0) }
|
|
169
|
+
* const pan = usePan({
|
|
170
|
+
* onUpdate: (event) => {
|
|
171
|
+
* 'worklet'
|
|
172
|
+
* camera.x.value += event.change.x
|
|
173
|
+
* camera.y.value += event.change.y
|
|
174
|
+
* },
|
|
175
|
+
* })
|
|
176
|
+
*
|
|
177
|
+
* return (
|
|
178
|
+
* <GestureDetector gesture={pan.gesture}>
|
|
179
|
+
* <Animated.View style={style} />
|
|
180
|
+
* </GestureDetector>
|
|
181
|
+
* )
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
184
|
+
* **`usePan` reports movement; `useDrag` owns a position.** That is the whole
|
|
185
|
+
* difference, and it decides which one a screen wants. `useDrag` holds `x`
|
|
186
|
+
* and `y` as the place a thing sits: they accumulate across gestures, they
|
|
187
|
+
* are clamped by `bounds`, and `elastic` bends them. `usePan` holds no
|
|
188
|
+
* position at all — it hands over `translation`, `velocity`, and a per-frame
|
|
189
|
+
* `change`, and the consumer advances whatever it drives. Reach for `useDrag`
|
|
190
|
+
* to move a view. Reach for this one to pan a camera, scrub a value, or feed
|
|
191
|
+
* a number Impulse has no business clamping.
|
|
192
|
+
*
|
|
193
|
+
* **Activation criteria.** `threshold` defaults to 10 points. With
|
|
194
|
+
* `axis: 'x'` or `'y'` it is directional, so a horizontal pan inside a
|
|
195
|
+
* vertical `ScrollView` leaves the scroll alone until the finger commits
|
|
196
|
+
* sideways; with `'both'` it is a radial distance. The device sweep did not
|
|
197
|
+
* test the default.
|
|
198
|
+
*
|
|
199
|
+
* **Coexistence.** A threshold decides who moves first; it does not decide
|
|
200
|
+
* who wins a contested touch. Say which gesture the touch belongs to as well
|
|
201
|
+
* — `deferTo` for a pan that is the fallback, `blocks` for one that is the
|
|
202
|
+
* foreground affordance, `alongside` for a pan that shares the touch with a
|
|
203
|
+
* pinch.
|
|
204
|
+
*
|
|
205
|
+
* **Web.** RNGH recognizes pan from pointer events, so a mouse drag behaves
|
|
206
|
+
* the same as a touch drag and `velocity` is reported in the same units. A
|
|
207
|
+
* trackpad's momentum scroll is not a pan and never reaches this hook.
|
|
208
|
+
* `pointers` above 1 is unreliable on web.
|
|
209
|
+
*
|
|
210
|
+
* **Accessibility.** A pan is invisible to a screen reader and unreachable
|
|
211
|
+
* from a keyboard, and this hook does not fix that. Whatever the pan moves
|
|
212
|
+
* must be reachable another way: buttons that step the value, a reset
|
|
213
|
+
* control for a panned canvas, or `accessibilityActions` with
|
|
214
|
+
* `onAccessibilityAction`. A pan-only affordance is a bug, not a trade-off.
|
|
215
|
+
*
|
|
216
|
+
* @param options - Activation criteria, callbacks, and the `alongside` /
|
|
217
|
+
* `blocks` / `deferTo` coexistence options every Impulse hook accepts.
|
|
218
|
+
*/
|
|
219
|
+
declare function usePan(options?: UsePanOptions): UsePanResult;
|
|
220
|
+
|
|
221
|
+
export { type PanAxis, type PanEvent, type UsePanOptions, type UsePanResult, usePan };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { PinchGesture } from 'react-native-gesture-handler';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
4
|
+
import { P as Point, H as HitSlop, I as IntentEndInfo, c as IntentResult } from '../types-ChGKY28a.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/** The intent-shaped payload a {@link usePinch} callback receives. */
|
|
8
|
+
interface PinchEvent {
|
|
9
|
+
/**
|
|
10
|
+
* How much the thing is scaled now — the same number as `scale`, after
|
|
11
|
+
* `min`, `max` and `elastic` were applied.
|
|
12
|
+
*
|
|
13
|
+
* This accumulates across gestures. A second pinch continues from where the
|
|
14
|
+
* first one stopped, which is why `min` and `max` can be written as the
|
|
15
|
+
* zoom range of the whole viewer rather than of one gesture.
|
|
16
|
+
*/
|
|
17
|
+
readonly scale: number;
|
|
18
|
+
/**
|
|
19
|
+
* How much the fingers scaled during **this gesture alone**, raw.
|
|
20
|
+
*
|
|
21
|
+
* Untouched by `min`, `max` and `elastic`, and reset to `1` at the start of
|
|
22
|
+
* every gesture. This is RNGH's own `scale`. Read `scale` for how big the
|
|
23
|
+
* thing being pinched actually is.
|
|
24
|
+
*/
|
|
25
|
+
readonly gestureScale: number;
|
|
26
|
+
/**
|
|
27
|
+
* The midpoint between the fingers, relative to the view.
|
|
28
|
+
*
|
|
29
|
+
* This is the point the zoom must happen about. A pinch scaled about the
|
|
30
|
+
* view's centre instead slides the content out from under the fingers, and
|
|
31
|
+
* that is the defect this field exists to prevent.
|
|
32
|
+
*/
|
|
33
|
+
readonly focal: Point;
|
|
34
|
+
/** How fast the scale is changing, in scale units per second. */
|
|
35
|
+
readonly velocity: number;
|
|
36
|
+
/**
|
|
37
|
+
* The nearest scale inside `min` and `max`. Equal to `scale` whenever the
|
|
38
|
+
* pinch is in range, which with the default `elastic` of `0` is always.
|
|
39
|
+
*
|
|
40
|
+
* With `elastic` set, the fingers can pull the scale past an end and
|
|
41
|
+
* Impulse leaves it there on release — moving it back is an animation, and
|
|
42
|
+
* Impulse owns no animation vocabulary. This field is the destination that
|
|
43
|
+
* animation needs, so the consumer does not have to re-derive the clamp
|
|
44
|
+
* from a range it already handed over.
|
|
45
|
+
*/
|
|
46
|
+
readonly settled: number;
|
|
47
|
+
/** How many fingers are down. */
|
|
48
|
+
readonly pointers: number;
|
|
49
|
+
}
|
|
50
|
+
/** Options for {@link usePinch}. */
|
|
51
|
+
interface UsePinchOptions extends GestureMemoOptions {
|
|
52
|
+
/**
|
|
53
|
+
* The scale before any pinch. Default `1`.
|
|
54
|
+
*
|
|
55
|
+
* Read once, at mount. `useSharedValue` keeps its first argument and
|
|
56
|
+
* ignores every later one, and that is the behaviour this option
|
|
57
|
+
* documents: after mount `scale` is the pinch's state, and the consumer
|
|
58
|
+
* moves it by writing it.
|
|
59
|
+
*/
|
|
60
|
+
initial?: number;
|
|
61
|
+
/**
|
|
62
|
+
* The smallest scale. Unset by default, which lets the fingers shrink the
|
|
63
|
+
* thing without limit.
|
|
64
|
+
*
|
|
65
|
+
* Set it to `1` for a viewer that may zoom in but never out.
|
|
66
|
+
*/
|
|
67
|
+
min?: number;
|
|
68
|
+
/**
|
|
69
|
+
* The largest scale. Unset by default, which lets the fingers grow the
|
|
70
|
+
* thing without limit.
|
|
71
|
+
*/
|
|
72
|
+
max?: number;
|
|
73
|
+
/**
|
|
74
|
+
* How much of the travel past `min` or `max` reaches `scale`, from `0` to
|
|
75
|
+
* `1`. Default `0`.
|
|
76
|
+
*
|
|
77
|
+
* `0` stops dead at the end. `1` ignores the end while the fingers are
|
|
78
|
+
* down. Anything between is resistance — the pinch keeps moving and moves
|
|
79
|
+
* less than the fingers do.
|
|
80
|
+
*
|
|
81
|
+
* Impulse does not bring the scale back. The end callback carries
|
|
82
|
+
* `settled` — the scale to animate to — and `@rootnative/inertia` or a
|
|
83
|
+
* `withSpring` of your own does the rest.
|
|
84
|
+
*/
|
|
85
|
+
elastic?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Extra touchable area around the view, in points.
|
|
88
|
+
*
|
|
89
|
+
* Written inline as an object is fine — the gesture is not rebuilt when the
|
|
90
|
+
* contents are unchanged.
|
|
91
|
+
*/
|
|
92
|
+
hitSlop?: HitSlop;
|
|
93
|
+
/**
|
|
94
|
+
* Whether the gesture is recognized at all. Default `true`.
|
|
95
|
+
*
|
|
96
|
+
* Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
|
|
97
|
+
* keeps its identity and its relations, so re-enabling it does not
|
|
98
|
+
* re-attach anything.
|
|
99
|
+
*/
|
|
100
|
+
enabled?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The pinch now owns the touch. **Runs on the JS thread** — Impulse owns
|
|
103
|
+
* the `scheduleOnRN` boundary, so this is an ordinary function and may
|
|
104
|
+
* touch React state.
|
|
105
|
+
*
|
|
106
|
+
* This is the first moment the pinch has definitely won. `onBegin` fires
|
|
107
|
+
* earlier and promises nothing.
|
|
108
|
+
*/
|
|
109
|
+
onPinchStart?: (event: PinchEvent) => void;
|
|
110
|
+
/**
|
|
111
|
+
* The pinch is over. **Runs on the JS thread.**
|
|
112
|
+
*
|
|
113
|
+
* Fires only for a pinch that activated, so a touch that never became a
|
|
114
|
+
* pinch never reaches here on either path.
|
|
115
|
+
*
|
|
116
|
+
* It fires for both endings, and `cancelled` says which. `false` is the
|
|
117
|
+
* fingers lifting, and `velocity` then describes the release. `true` is the
|
|
118
|
+
* system taking the pinch away — a competing gesture won, or the app went
|
|
119
|
+
* to the background. **There was no release on that path, so the velocity
|
|
120
|
+
* describes the last movement rather than a throw.**
|
|
121
|
+
*
|
|
122
|
+
* Read `settled` on both paths for where an elastic overshoot belongs.
|
|
123
|
+
*/
|
|
124
|
+
onPinchEnd?: (event: PinchEvent, info: IntentEndInfo) => void;
|
|
125
|
+
/**
|
|
126
|
+
* A finger went down and the gesture is now a candidate. **This is a
|
|
127
|
+
* worklet** — mark it with the `'worklet'` directive, and do not touch
|
|
128
|
+
* React state from it.
|
|
129
|
+
*
|
|
130
|
+
* Being a candidate is not the same as winning: one finger is not a pinch,
|
|
131
|
+
* and a second one may never arrive. Undo whatever it sets in
|
|
132
|
+
* `onFinalize`.
|
|
133
|
+
*/
|
|
134
|
+
onBegin?: (event: PinchEvent) => void;
|
|
135
|
+
/**
|
|
136
|
+
* The scale changed. **This is a worklet**, and it runs on every frame the
|
|
137
|
+
* fingers move.
|
|
138
|
+
*
|
|
139
|
+
* `scale` is already written by the time this runs, so a `useAnimatedStyle`
|
|
140
|
+
* reading it needs nothing from here. Use this for the work that scaling
|
|
141
|
+
* alone does not do — holding the focal point still, say.
|
|
142
|
+
*
|
|
143
|
+
* There is no JS-thread counterpart on purpose. A per-frame `scheduleOnRN`
|
|
144
|
+
* is a scheduling cost paid sixty times a second for a value that is
|
|
145
|
+
* already on the thread that needs it.
|
|
146
|
+
*/
|
|
147
|
+
onUpdate?: (event: PinchEvent) => void;
|
|
148
|
+
/**
|
|
149
|
+
* The gesture is over, whether it activated or not. **This is a worklet.**
|
|
150
|
+
*
|
|
151
|
+
* `success` is `true` when the pinch activated and ended normally. This is
|
|
152
|
+
* the right place to clear anything `onBegin` set, because it runs on both
|
|
153
|
+
* paths.
|
|
154
|
+
*/
|
|
155
|
+
onFinalize?: (event: PinchEvent, success: boolean) => void;
|
|
156
|
+
}
|
|
157
|
+
/** What {@link usePinch} returns. */
|
|
158
|
+
interface UsePinchResult extends IntentResult<PinchGesture> {
|
|
159
|
+
/**
|
|
160
|
+
* How much the thing is scaled, after `min`, `max` and `elastic`.
|
|
161
|
+
*
|
|
162
|
+
* **This is a position, not a movement.** It accumulates across gestures,
|
|
163
|
+
* so a second pinch continues from where the first stopped. `usePan` is the
|
|
164
|
+
* hook whose value zeroes at every gesture.
|
|
165
|
+
*
|
|
166
|
+
* Writing it is allowed and is how a release animation, or a reset button,
|
|
167
|
+
* hands control back. The next pinch continues from whatever it holds.
|
|
168
|
+
*/
|
|
169
|
+
readonly scale: SharedValue<number>;
|
|
170
|
+
/**
|
|
171
|
+
* The midpoint between the fingers, relative to the view.
|
|
172
|
+
*
|
|
173
|
+
* It keeps the last gesture's focal point after the fingers lift, so a
|
|
174
|
+
* release animation scales about the same place the pinch did rather than
|
|
175
|
+
* snapping to the origin.
|
|
176
|
+
*/
|
|
177
|
+
readonly focal: SharedValue<Point>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Recognize a two-finger pinch, and own the scale it produces.
|
|
181
|
+
*
|
|
182
|
+
* ```tsx
|
|
183
|
+
* const pinch = usePinch({ min: 1, max: 4 })
|
|
184
|
+
*
|
|
185
|
+
* const style = useAnimatedStyle(() => ({
|
|
186
|
+
* transform: [{ scale: pinch.scale.value }],
|
|
187
|
+
* }))
|
|
188
|
+
*
|
|
189
|
+
* return (
|
|
190
|
+
* <GestureDetector gesture={pinch.gesture}>
|
|
191
|
+
* <Animated.Image style={style} source={source} />
|
|
192
|
+
* </GestureDetector>
|
|
193
|
+
* )
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* **The scale accumulates; RNGH's does not.** A bare `Gesture.Pinch()`
|
|
197
|
+
* reports a factor that restarts at `1` on every gesture, so a viewer built
|
|
198
|
+
* on it snaps back to its original size the moment the fingers lift again.
|
|
199
|
+
* Carrying the scale across gestures is the multiplication and the stored
|
|
200
|
+
* start that every consumer otherwise writes, and it is why `min` and `max`
|
|
201
|
+
* can be the range of the viewer rather than of one gesture.
|
|
202
|
+
*
|
|
203
|
+
* **`focal` is the field a zoom viewer cannot skip.** Scaling about the
|
|
204
|
+
* view's centre slides the content out from under the fingers. The focal
|
|
205
|
+
* point is where the zoom has to happen, and it is reported relative to the
|
|
206
|
+
* view so it can go straight into a `translate` / `scale` / `translate`
|
|
207
|
+
* transform.
|
|
208
|
+
*
|
|
209
|
+
* **Activation criteria.** There are none to set. RNGH's pinch takes the
|
|
210
|
+
* touch as soon as a second finger moves, and it exposes no threshold, so
|
|
211
|
+
* Impulse has none to pass on. This is the one continuous intent whose
|
|
212
|
+
* coexistence is decided entirely by relations — see below.
|
|
213
|
+
*
|
|
214
|
+
* **Coexistence.** A pinch almost always shares its view with a pan, and it
|
|
215
|
+
* has no threshold to separate them with. Say it: `alongside` on both, so the
|
|
216
|
+
* two recognize at once and the same two fingers can move and scale the
|
|
217
|
+
* thing. `useGestures` with `mode: 'simultaneous'` is the same statement for
|
|
218
|
+
* gestures this screen owns.
|
|
219
|
+
*
|
|
220
|
+
* **Web.** RNGH recognizes pinch from pointer events, so it needs two
|
|
221
|
+
* pointers — a touchscreen or a device that reports them. A trackpad's pinch
|
|
222
|
+
* arrives as a `wheel` event with `ctrlKey`, which is not a pointer pair and
|
|
223
|
+
* never reaches this hook, so a desktop browser with a trackpad alone cannot
|
|
224
|
+
* zoom. Give it a control that sets `scale` directly, which the
|
|
225
|
+
* accessibility fallback needs anyway. The browser's own page zoom is
|
|
226
|
+
* unaffected either way.
|
|
227
|
+
*
|
|
228
|
+
* **Accessibility.** A pinch is invisible to a screen reader and unreachable
|
|
229
|
+
* from a keyboard, and this hook does not fix that. Whatever the pinch scales
|
|
230
|
+
* must be reachable another way: zoom-in and zoom-out buttons that write
|
|
231
|
+
* `scale`, a control that resets it, or `accessibilityActions` with
|
|
232
|
+
* `onAccessibilityAction`. A pinch-only zoom is a bug, not a trade-off.
|
|
233
|
+
*
|
|
234
|
+
* @param options - The scale range, callbacks, and the `alongside` /
|
|
235
|
+
* `blocks` / `deferTo` coexistence options every Impulse hook accepts.
|
|
236
|
+
*/
|
|
237
|
+
declare function usePinch(options?: UsePinchOptions): UsePinchResult;
|
|
238
|
+
|
|
239
|
+
export { type PinchEvent, type UsePinchOptions, type UsePinchResult, usePinch };
|
package/dist/raw/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DependencyList } from 'react';
|
|
2
2
|
import { GestureType } from 'react-native-gesture-handler';
|
|
3
|
-
import { B as BuiltGesture, G as GestureMemoOptions } from '../useGestureMemo-
|
|
4
|
-
import '../types-
|
|
3
|
+
import { B as BuiltGesture, G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
4
|
+
import '../types-ChGKY28a.js';
|
|
5
5
|
import 'react-native-reanimated';
|
|
6
6
|
|
|
7
7
|
/** Options for {@link useRawGesture}. */
|
|
@@ -35,7 +35,7 @@ type RawGestureResult<G extends GestureType> = BuiltGesture<G>;
|
|
|
35
35
|
* put it through `useLatestCallback` and depend on the stable result.
|
|
36
36
|
* - **The thread.** RNGH decides per callback, by whether it carries the
|
|
37
37
|
* `'worklet'` directive, and warns in development when a gesture mixes the
|
|
38
|
-
* two. Impulse does not insert a `
|
|
38
|
+
* two. Impulse does not insert a `scheduleOnRN` boundary for you here; that is
|
|
39
39
|
* something the intent hooks do because they know what each callback means.
|
|
40
40
|
* - **The payload.** You get RNGH's flat event, not an intent-shaped one.
|
|
41
41
|
*
|
package/dist/raw/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { useRawGesture } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { useRawGesture } from '../chunk-HGBCIL6X.js';
|
|
2
|
+
import '../chunk-NYDDZD4G.js';
|
|
3
3
|
import '../chunk-ZX7WNICB.js';
|