@revenuecat/purchases-ui-js 4.14.0 → 4.14.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.
|
@@ -224,7 +224,12 @@
|
|
|
224
224
|
|
|
225
225
|
let startTranslation = $state(0);
|
|
226
226
|
|
|
227
|
+
// Releasing after holding still this long is not a flick, however fast the drag was.
|
|
228
|
+
const FLICK_MAX_PAUSE_MS = 100;
|
|
229
|
+
|
|
227
230
|
let lastClientX = 0;
|
|
231
|
+
let lastMoveTimeStamp = 0;
|
|
232
|
+
let velocityPxPerMs: number | null = null;
|
|
228
233
|
|
|
229
234
|
function onpointerdown(event: PointerEvent) {
|
|
230
235
|
const target = event.currentTarget as HTMLElement;
|
|
@@ -232,9 +237,21 @@
|
|
|
232
237
|
startTranslation = getTranslation(slider);
|
|
233
238
|
gestureStartPage = currentPage;
|
|
234
239
|
lastClientX = event.clientX;
|
|
240
|
+
lastMoveTimeStamp = event.timeStamp;
|
|
241
|
+
velocityPxPerMs = null;
|
|
235
242
|
}
|
|
236
243
|
|
|
237
244
|
function onpointerup(event: PointerEvent) {
|
|
245
|
+
const stalled = event.timeStamp - lastMoveTimeStamp > FLICK_MAX_PAUSE_MS;
|
|
246
|
+
settleGesture(event, stalled ? 0 : (velocityPxPerMs ?? 0));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Fires when the browser takes over the touch (e.g. vertical scroll).
|
|
250
|
+
function onpointercancel(event: PointerEvent) {
|
|
251
|
+
settleGesture(event, 0);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function settleGesture(event: PointerEvent, releaseVelocityPxPerMs: number) {
|
|
238
255
|
const target = event.currentTarget as HTMLElement;
|
|
239
256
|
if (target.hasPointerCapture(event.pointerId)) {
|
|
240
257
|
target.releasePointerCapture(event.pointerId);
|
|
@@ -242,12 +259,10 @@
|
|
|
242
259
|
|
|
243
260
|
const translation = getTranslation(slider);
|
|
244
261
|
|
|
245
|
-
// Advance at most one page from where the gesture started, based on how
|
|
246
|
-
// far the finger travelled relative to a page's width (see
|
|
247
|
-
// getSwipeTargetPage).
|
|
248
262
|
const page = getSwipeTargetPage({
|
|
249
263
|
startPage: gestureStartPage ?? getPageFromTranslation(translation),
|
|
250
264
|
dragDistance: translation - startTranslation,
|
|
265
|
+
velocityPxPerMs: releaseVelocityPxPerMs,
|
|
251
266
|
pageWidth,
|
|
252
267
|
pageCount: pages.length,
|
|
253
268
|
loop: carouselPagingLoop,
|
|
@@ -284,7 +299,17 @@
|
|
|
284
299
|
// for touch-derived pointer events in Safari and device-pixel scaled in
|
|
285
300
|
// some Chrome configurations.
|
|
286
301
|
const deltaX = event.clientX - lastClientX;
|
|
302
|
+
// At least 1ms: synthetic input and coarse timestamps can report 0ms.
|
|
303
|
+
const deltaT = Math.max(1, event.timeStamp - lastMoveTimeStamp);
|
|
287
304
|
lastClientX = event.clientX;
|
|
305
|
+
lastMoveTimeStamp = event.timeStamp;
|
|
306
|
+
// Moving average, so one jittery pointermove doesn't decide the release
|
|
307
|
+
// speed; the first sample seeds it so short flicks aren't underestimated.
|
|
308
|
+
const sampleVelocity = deltaX / deltaT;
|
|
309
|
+
velocityPxPerMs =
|
|
310
|
+
velocityPxPerMs === null
|
|
311
|
+
? sampleVelocity
|
|
312
|
+
: 0.6 * velocityPxPerMs + 0.4 * sampleVelocity;
|
|
288
313
|
|
|
289
314
|
const [min, max] = carouselPagingLoop
|
|
290
315
|
? [startTranslation - carouselWidth, startTranslation + carouselWidth]
|
|
@@ -330,6 +355,7 @@
|
|
|
330
355
|
{ontransitionend}
|
|
331
356
|
{onpointerdown}
|
|
332
357
|
{onpointerup}
|
|
358
|
+
{onpointercancel}
|
|
333
359
|
{onpointermove}
|
|
334
360
|
class="slider"
|
|
335
361
|
style={sliderStyle}
|
|
@@ -7,35 +7,39 @@ export declare function clamp(value: number, min: number, max: number): number;
|
|
|
7
7
|
* `Array.prototype.at` so Chrome < 92 does not throw while mounting.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getDefaultPageIndex<T>(pages: readonly T[], initialPageIndex: number): number;
|
|
10
|
+
/** Release speed (0.4 px/ms = 400 px/s) at or above which a swipe counts as a flick. */
|
|
11
|
+
export declare const FLICK_VELOCITY_PX_PER_MS = 0.4;
|
|
10
12
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*
|
|
13
|
+
* Decides which page a swipe settles on when the pointer is released. Matches
|
|
14
|
+
* the native (iOS and Android) SDK carousels:
|
|
15
|
+
*
|
|
16
|
+
* - A flick (release speed >= `FLICK_VELOCITY_PX_PER_MS`) moves in the
|
|
17
|
+
* direction the pointer was travelling when released. From a partial drag it
|
|
18
|
+
* lands on the next page boundary that way, so dragging forward then flicking
|
|
19
|
+
* back returns to the start page.
|
|
20
|
+
* - A slower release settles on whichever page is nearest, i.e. the drag must
|
|
21
|
+
* pass `SLOW_DRAG_THRESHOLD` of a page's width to change page.
|
|
22
|
+
* - The result is never more than one page from `startPage`.
|
|
18
23
|
*
|
|
19
|
-
* Distance-based (not velocity-based), mirroring the iOS SDK's `handleDragEnd`:
|
|
20
|
-
* a drag past `PAGE_ADVANCE_THRESHOLD` of a page's width advances exactly one
|
|
21
|
-
* page in the swipe direction regardless of speed; a shorter drag stays put.
|
|
22
24
|
* Non-loop carousels clamp at their first and last page; loop carousels may
|
|
23
25
|
* return an index just outside `[0, pageCount)` so the caller can animate into
|
|
24
26
|
* a clone before normalizing.
|
|
25
27
|
*
|
|
26
|
-
* @param startPage
|
|
27
|
-
* @param dragDistance
|
|
28
|
-
*
|
|
29
|
-
* @param
|
|
30
|
-
*
|
|
31
|
-
* @param
|
|
28
|
+
* @param startPage Page the gesture started on.
|
|
29
|
+
* @param dragDistance Signed change in slider translation in px (negative =
|
|
30
|
+
* pointer moved left / next page).
|
|
31
|
+
* @param velocityPxPerMs Signed pointer velocity at release, same sign
|
|
32
|
+
* convention as `dragDistance`.
|
|
33
|
+
* @param pageWidth Width of a single page in px.
|
|
34
|
+
* @param pageCount Total number of real pages.
|
|
35
|
+
* @param loop Whether the carousel loops.
|
|
32
36
|
*/
|
|
33
|
-
export declare function getSwipeTargetPage({ startPage, dragDistance, pageWidth, pageCount, loop,
|
|
37
|
+
export declare function getSwipeTargetPage({ startPage, dragDistance, velocityPxPerMs, pageWidth, pageCount, loop, }: {
|
|
34
38
|
startPage: number;
|
|
35
39
|
dragDistance: number;
|
|
40
|
+
velocityPxPerMs: number;
|
|
36
41
|
pageWidth: number;
|
|
37
42
|
pageCount: number;
|
|
38
43
|
loop: boolean;
|
|
39
|
-
advanceThreshold?: number;
|
|
40
44
|
}): number;
|
|
41
45
|
export declare function getTranslation(element: HTMLElement | null): number;
|
|
@@ -19,40 +19,54 @@ export function clamp(value, min, max) {
|
|
|
19
19
|
export function getDefaultPageIndex(pages, initialPageIndex) {
|
|
20
20
|
return pages[initialPageIndex] !== undefined ? initialPageIndex : 0;
|
|
21
21
|
}
|
|
22
|
+
/** Fraction of a page's width a slow drag must pass to change page. */
|
|
23
|
+
const SLOW_DRAG_THRESHOLD = 0.5;
|
|
24
|
+
/** Release speed (0.4 px/ms = 400 px/s) at or above which a swipe counts as a flick. */
|
|
25
|
+
export const FLICK_VELOCITY_PX_PER_MS = 0.4;
|
|
22
26
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*
|
|
27
|
+
* Decides which page a swipe settles on when the pointer is released. Matches
|
|
28
|
+
* the native (iOS and Android) SDK carousels:
|
|
29
|
+
*
|
|
30
|
+
* - A flick (release speed >= `FLICK_VELOCITY_PX_PER_MS`) moves in the
|
|
31
|
+
* direction the pointer was travelling when released. From a partial drag it
|
|
32
|
+
* lands on the next page boundary that way, so dragging forward then flicking
|
|
33
|
+
* back returns to the start page.
|
|
34
|
+
* - A slower release settles on whichever page is nearest, i.e. the drag must
|
|
35
|
+
* pass `SLOW_DRAG_THRESHOLD` of a page's width to change page.
|
|
36
|
+
* - The result is never more than one page from `startPage`.
|
|
30
37
|
*
|
|
31
|
-
* Distance-based (not velocity-based), mirroring the iOS SDK's `handleDragEnd`:
|
|
32
|
-
* a drag past `PAGE_ADVANCE_THRESHOLD` of a page's width advances exactly one
|
|
33
|
-
* page in the swipe direction regardless of speed; a shorter drag stays put.
|
|
34
38
|
* Non-loop carousels clamp at their first and last page; loop carousels may
|
|
35
39
|
* return an index just outside `[0, pageCount)` so the caller can animate into
|
|
36
40
|
* a clone before normalizing.
|
|
37
41
|
*
|
|
38
|
-
* @param startPage
|
|
39
|
-
* @param dragDistance
|
|
40
|
-
*
|
|
41
|
-
* @param
|
|
42
|
-
*
|
|
43
|
-
* @param
|
|
42
|
+
* @param startPage Page the gesture started on.
|
|
43
|
+
* @param dragDistance Signed change in slider translation in px (negative =
|
|
44
|
+
* pointer moved left / next page).
|
|
45
|
+
* @param velocityPxPerMs Signed pointer velocity at release, same sign
|
|
46
|
+
* convention as `dragDistance`.
|
|
47
|
+
* @param pageWidth Width of a single page in px.
|
|
48
|
+
* @param pageCount Total number of real pages.
|
|
49
|
+
* @param loop Whether the carousel loops.
|
|
44
50
|
*/
|
|
45
|
-
export function getSwipeTargetPage({ startPage, dragDistance, pageWidth, pageCount, loop,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
export function getSwipeTargetPage({ startPage, dragDistance, velocityPxPerMs, pageWidth, pageCount, loop, }) {
|
|
52
|
+
// Pages travelled from startPage; positive = towards the next page.
|
|
53
|
+
const offset = -dragDistance / pageWidth;
|
|
54
|
+
// A flick lands on the next whole page past the current offset in its
|
|
55
|
+
// direction; from a page-aligned start that is the adjacent page.
|
|
56
|
+
let pageDelta;
|
|
57
|
+
if (velocityPxPerMs <= -FLICK_VELOCITY_PX_PER_MS) {
|
|
58
|
+
pageDelta = Math.floor(offset) + 1;
|
|
59
|
+
}
|
|
60
|
+
else if (velocityPxPerMs >= FLICK_VELOCITY_PX_PER_MS) {
|
|
61
|
+
pageDelta = Math.ceil(offset) - 1;
|
|
62
|
+
}
|
|
63
|
+
else if (Math.abs(offset) > SLOW_DRAG_THRESHOLD) {
|
|
64
|
+
pageDelta = Math.sign(offset);
|
|
51
65
|
}
|
|
52
|
-
else
|
|
53
|
-
|
|
54
|
-
page -= 1;
|
|
66
|
+
else {
|
|
67
|
+
pageDelta = 0;
|
|
55
68
|
}
|
|
69
|
+
const page = startPage + clamp(pageDelta, -1, 1);
|
|
56
70
|
return loop ? page : clamp(page, 0, pageCount - 1);
|
|
57
71
|
}
|
|
58
72
|
export function getTranslation(element) {
|