gesturx 1.0.0 → 1.2.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 +17 -1
- package/dist/index.d.ts +192 -0
- package/dist/index.js +627 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +2 -0
- package/dist/index.umd.cjs.map +1 -0
- package/package.json +12 -1
- package/.github/workflows/publish.yml +0 -17
- package/.prettierrc.json +0 -3
- package/eslint.config.js +0 -18
- package/index.html +0 -13
- package/src/core/index.ts +0 -4
- package/src/core/manager.ts +0 -46
- package/src/core/pan/PanRecognizer.ts +0 -192
- package/src/core/pinch/PinchRecognizer.ts +0 -131
- package/src/core/recognizer/Recognizer.ts +0 -54
- package/src/core/recognizer/RecognizerEvent.ts +0 -39
- package/src/core/recognizer/mapToRecognizerEvent.ts +0 -142
- package/src/core/rotate/RotateRecognizer.ts +0 -115
- package/src/core/swipe/SwipeRecognizer.test.ts +0 -160
- package/src/core/swipe/SwipeRecognizer.ts +0 -82
- package/src/core/swipe/isRecognizedAsSwipe.ts +0 -13
- package/src/core/tap/TapRecognizer.test.ts +0 -567
- package/src/core/tap/TapRecognizer.ts +0 -172
- package/src/core/utils/events.ts +0 -186
- package/src/core/utils/geometry.ts +0 -115
- package/src/core/utils/utils.test.ts +0 -26
- package/src/core/utils/utils.ts +0 -58
- package/src/demo/App.tsx +0 -171
- package/src/demo/DebugBox.tsx +0 -10
- package/src/demo/useManager.ts +0 -40
- package/src/demo/useTap.tsx +0 -87
- package/src/index.css +0 -66
- package/src/main.tsx +0 -14
- package/src/theme.ts +0 -11
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -26
- package/tsconfig.node.json +0 -11
- package/vite.config.ts +0 -38
- package/vitest.config.ts +0 -8
- /package/{public → dist}/vite.svg +0 -0
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Observable,
|
|
3
|
-
combineLatest,
|
|
4
|
-
distinctUntilChanged,
|
|
5
|
-
filter,
|
|
6
|
-
first,
|
|
7
|
-
map,
|
|
8
|
-
merge,
|
|
9
|
-
mergeMap,
|
|
10
|
-
scan,
|
|
11
|
-
switchMap,
|
|
12
|
-
takeUntil,
|
|
13
|
-
} from "rxjs"
|
|
14
|
-
import { RecognizerEvent } from "../recognizer/RecognizerEvent"
|
|
15
|
-
import { Recognizer } from "../recognizer/Recognizer"
|
|
16
|
-
import { PanEvent, PanRecognizer } from "../pan/PanRecognizer"
|
|
17
|
-
|
|
18
|
-
export interface PinchEvent extends RecognizerEvent {
|
|
19
|
-
type: "pinchStart" | "pinchMove" | "pinchEnd"
|
|
20
|
-
scale: number
|
|
21
|
-
/**
|
|
22
|
-
* Distance between start and current
|
|
23
|
-
*/
|
|
24
|
-
distance: number
|
|
25
|
-
/**
|
|
26
|
-
* Delta distance between events
|
|
27
|
-
*/
|
|
28
|
-
deltaDistance: number
|
|
29
|
-
/**
|
|
30
|
-
* Delta scale between events
|
|
31
|
-
*/
|
|
32
|
-
deltaDistanceScale: number
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type Options = {
|
|
36
|
-
posThreshold?: number
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class PinchRecognizer extends Recognizer<Options> {
|
|
40
|
-
public events$: Observable<PinchEvent>
|
|
41
|
-
|
|
42
|
-
constructor(protected options: Options = {}) {
|
|
43
|
-
super(options)
|
|
44
|
-
|
|
45
|
-
this.events$ = this.config$.pipe(
|
|
46
|
-
switchMap((initializedWith) => {
|
|
47
|
-
const { posThreshold } = initializedWith.options ?? {}
|
|
48
|
-
const panRecognizer = new PanRecognizer({ posThreshold })
|
|
49
|
-
|
|
50
|
-
panRecognizer.initialize(initializedWith)
|
|
51
|
-
|
|
52
|
-
const hasLessThanTwoFinger$ = panRecognizer.events$.pipe(
|
|
53
|
-
filter(({ pointers }) => pointers.length < 2),
|
|
54
|
-
distinctUntilChanged(),
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
const hasMoreThanOneFinger$ = panRecognizer.events$.pipe(
|
|
58
|
-
map((event) => [event, event.pointers.length > 1] as const),
|
|
59
|
-
distinctUntilChanged(
|
|
60
|
-
(
|
|
61
|
-
[_, previousHasMoreThanOneFinger],
|
|
62
|
-
[__, currentHasMoreThanOneFinger],
|
|
63
|
-
) => previousHasMoreThanOneFinger === currentHasMoreThanOneFinger,
|
|
64
|
-
),
|
|
65
|
-
filter(([_, hasMoreThanOneFinger]) => hasMoreThanOneFinger),
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
const start$ = hasMoreThanOneFinger$.pipe(
|
|
69
|
-
map(([event]) => ({
|
|
70
|
-
...event,
|
|
71
|
-
type: "pinchStart" as const,
|
|
72
|
-
scale: 1,
|
|
73
|
-
distance: 0,
|
|
74
|
-
deltaDistance: 0,
|
|
75
|
-
deltaDistanceScale: 1,
|
|
76
|
-
})),
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
const rotate$ = start$.pipe(
|
|
80
|
-
mergeMap((startEvent) => {
|
|
81
|
-
return panRecognizer.events$.pipe(
|
|
82
|
-
scan<PanEvent, PinchEvent, Partial<PinchEvent>>(
|
|
83
|
-
(acc, current) => {
|
|
84
|
-
const previousPointersAverageDistance =
|
|
85
|
-
acc.pointersAverageDistance ??
|
|
86
|
-
current.pointersAverageDistance
|
|
87
|
-
|
|
88
|
-
const scale =
|
|
89
|
-
current.pointersAverageDistance /
|
|
90
|
-
startEvent.pointersAverageDistance
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
...acc,
|
|
94
|
-
...current,
|
|
95
|
-
scale,
|
|
96
|
-
distance:
|
|
97
|
-
current.pointersAverageDistance -
|
|
98
|
-
startEvent.pointersAverageDistance,
|
|
99
|
-
deltaDistance:
|
|
100
|
-
current.pointersAverageDistance -
|
|
101
|
-
previousPointersAverageDistance,
|
|
102
|
-
deltaDistanceScale:
|
|
103
|
-
current.pointersAverageDistance /
|
|
104
|
-
previousPointersAverageDistance,
|
|
105
|
-
type: "pinchMove" as const,
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
{ scale: 0 },
|
|
109
|
-
),
|
|
110
|
-
takeUntil(hasLessThanTwoFinger$),
|
|
111
|
-
)
|
|
112
|
-
}),
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
const end$ = start$.pipe(
|
|
116
|
-
mergeMap(() =>
|
|
117
|
-
combineLatest([rotate$, hasLessThanTwoFinger$] as const).pipe(
|
|
118
|
-
first(),
|
|
119
|
-
map(([event]) => ({
|
|
120
|
-
...event,
|
|
121
|
-
type: "pinchEnd" as const,
|
|
122
|
-
})),
|
|
123
|
-
),
|
|
124
|
-
),
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
return merge(start$, rotate$, end$)
|
|
128
|
-
}),
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { BehaviorSubject, Observable, filter } from "rxjs"
|
|
2
|
-
|
|
3
|
-
type RecognizerConfig<Options> = {
|
|
4
|
-
container?: HTMLElement
|
|
5
|
-
afterEventReceived?: (event: PointerEvent) => PointerEvent
|
|
6
|
-
options?: Options
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
type ValidRecognizerConfig<Options> = Required<
|
|
10
|
-
Pick<RecognizerConfig<Options>, "container">
|
|
11
|
-
> &
|
|
12
|
-
RecognizerConfig<Options>
|
|
13
|
-
|
|
14
|
-
export interface RecognizerOptions {
|
|
15
|
-
failWith?: { start$: Observable<unknown> }[]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class Recognizer<Options> {
|
|
19
|
-
protected config$ = new BehaviorSubject<RecognizerConfig<Options>>({})
|
|
20
|
-
protected validConfig$ = this.config$.pipe(
|
|
21
|
-
filter(
|
|
22
|
-
(config): config is ValidRecognizerConfig<Options> => !!config.container,
|
|
23
|
-
),
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
constructor(options: Options) {
|
|
27
|
-
this.update(options)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public initialize(config: RecognizerConfig<Options>) {
|
|
31
|
-
const prevConfig = this.config$.getValue()
|
|
32
|
-
|
|
33
|
-
this.config$.next({
|
|
34
|
-
...prevConfig,
|
|
35
|
-
...config,
|
|
36
|
-
options: {
|
|
37
|
-
...prevConfig.options,
|
|
38
|
-
...config.options,
|
|
39
|
-
} as Options,
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
public update(options: Options) {
|
|
44
|
-
const config = this.config$.getValue()
|
|
45
|
-
|
|
46
|
-
this.config$.next({
|
|
47
|
-
...config,
|
|
48
|
-
options: {
|
|
49
|
-
...config.options,
|
|
50
|
-
...options,
|
|
51
|
-
},
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export interface RecognizerEvent {
|
|
2
|
-
center: { x: number; y: number }
|
|
3
|
-
event: PointerEvent
|
|
4
|
-
/**
|
|
5
|
-
* Contain the last known pointer event for each active fingers.
|
|
6
|
-
* It should contain a unique list, with n being the number of active fingers.
|
|
7
|
-
*/
|
|
8
|
-
pointers: PointerEvent[]
|
|
9
|
-
/**
|
|
10
|
-
* Delay between the user action and the event recognition
|
|
11
|
-
*/
|
|
12
|
-
delay: number
|
|
13
|
-
deltaX: number
|
|
14
|
-
deltaY: number
|
|
15
|
-
/**
|
|
16
|
-
* @important
|
|
17
|
-
* Can be negative, 0, positive
|
|
18
|
-
*/
|
|
19
|
-
velocityX: number
|
|
20
|
-
/**
|
|
21
|
-
* @important
|
|
22
|
-
* Can be negative, 0, positive
|
|
23
|
-
*/
|
|
24
|
-
velocityY: number
|
|
25
|
-
startTime: number
|
|
26
|
-
/**
|
|
27
|
-
* Angle delta (in degrees) between pointers between this event
|
|
28
|
-
* and the previous one.
|
|
29
|
-
*
|
|
30
|
-
* @important
|
|
31
|
-
* - Will only register a delta if the previous event and current event
|
|
32
|
-
* have the same amount of fingers
|
|
33
|
-
*/
|
|
34
|
-
deltaPointersAngle: number
|
|
35
|
-
/**
|
|
36
|
-
* Average distance between all pointers
|
|
37
|
-
*/
|
|
38
|
-
pointersAverageDistance: number
|
|
39
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { Observable, map, scan } from "rxjs"
|
|
2
|
-
import { RecognizerEvent } from "./RecognizerEvent"
|
|
3
|
-
import { calculateVelocity } from "../utils/utils"
|
|
4
|
-
import {
|
|
5
|
-
calculateAngleDelta,
|
|
6
|
-
calculateAverageDistance,
|
|
7
|
-
calculateCentroid,
|
|
8
|
-
} from "../utils/geometry"
|
|
9
|
-
|
|
10
|
-
export interface RecognizerEventInput {
|
|
11
|
-
event: PointerEvent
|
|
12
|
-
latestActivePointers: PointerEvent[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface RecognizerEventState extends RecognizerEventInput {
|
|
16
|
-
startTime: number
|
|
17
|
-
center: { x: number; y: number }
|
|
18
|
-
deltaX: number
|
|
19
|
-
deltaY: number
|
|
20
|
-
deltaPointersAngle: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const calculateNewDelta = (
|
|
24
|
-
newCenter: { x: number; y: number },
|
|
25
|
-
prevCenter: { x: number; y: number },
|
|
26
|
-
prevDeltaX: number,
|
|
27
|
-
prevDeltaY: number,
|
|
28
|
-
) => {
|
|
29
|
-
const deltaX = newCenter.x - prevCenter.x + prevDeltaX
|
|
30
|
-
const deltaY = newCenter.y - prevCenter.y + prevDeltaY
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
deltaX,
|
|
34
|
-
deltaY,
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const mapToRecognizerEvent = <T extends RecognizerEventInput>(
|
|
39
|
-
stream: Observable<T>,
|
|
40
|
-
): Observable<RecognizerEvent> => {
|
|
41
|
-
const getCenterFromData = (data: T) =>
|
|
42
|
-
calculateCentroid(
|
|
43
|
-
!data.latestActivePointers.length
|
|
44
|
-
? [data.event]
|
|
45
|
-
: data.latestActivePointers,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
return stream.pipe(
|
|
49
|
-
scan<
|
|
50
|
-
T,
|
|
51
|
-
RecognizerEventState,
|
|
52
|
-
Pick<
|
|
53
|
-
RecognizerEventState,
|
|
54
|
-
"deltaX" | "center" | "deltaY" | "latestActivePointers"
|
|
55
|
-
>
|
|
56
|
-
>(
|
|
57
|
-
(acc, curr) => {
|
|
58
|
-
const newCenter = getCenterFromData(curr)
|
|
59
|
-
const prevCenter = acc.center
|
|
60
|
-
const prevDeltaX = acc.deltaX
|
|
61
|
-
const prevDeltaY = acc.deltaY
|
|
62
|
-
const prevActivePointersNumber = acc.latestActivePointers?.length ?? 0
|
|
63
|
-
const currActivePointersNumber = curr.latestActivePointers?.length ?? 0
|
|
64
|
-
|
|
65
|
-
const hasAddedOrRemovedAFinger =
|
|
66
|
-
[
|
|
67
|
-
"pointerdown",
|
|
68
|
-
"pointercancel",
|
|
69
|
-
"pointerleave",
|
|
70
|
-
"pointerup",
|
|
71
|
-
].includes(curr.event.type) &&
|
|
72
|
-
prevActivePointersNumber !== currActivePointersNumber
|
|
73
|
-
const hasAtLeastOneFinger = currActivePointersNumber > 0
|
|
74
|
-
|
|
75
|
-
const { deltaX, deltaY } =
|
|
76
|
-
hasAddedOrRemovedAFinger && hasAtLeastOneFinger
|
|
77
|
-
? calculateNewDelta(newCenter, newCenter, prevDeltaX, prevDeltaY)
|
|
78
|
-
: calculateNewDelta(newCenter, prevCenter, prevDeltaX, prevDeltaY)
|
|
79
|
-
|
|
80
|
-
const { degreesDelta: deltaPointersAngle } =
|
|
81
|
-
prevActivePointersNumber === currActivePointersNumber &&
|
|
82
|
-
prevActivePointersNumber >= 2 &&
|
|
83
|
-
currActivePointersNumber >= 2
|
|
84
|
-
? calculateAngleDelta(
|
|
85
|
-
acc.latestActivePointers ?? [],
|
|
86
|
-
curr.latestActivePointers ?? [],
|
|
87
|
-
)
|
|
88
|
-
: { degreesDelta: 0 }
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
startTime: new Date().getTime(),
|
|
92
|
-
...acc,
|
|
93
|
-
...curr,
|
|
94
|
-
center: newCenter,
|
|
95
|
-
deltaX,
|
|
96
|
-
deltaY,
|
|
97
|
-
deltaPointersAngle,
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
deltaX: 0,
|
|
102
|
-
deltaY: 0,
|
|
103
|
-
center: { x: 0, y: 0 },
|
|
104
|
-
latestActivePointers: [],
|
|
105
|
-
},
|
|
106
|
-
),
|
|
107
|
-
map((data) => {
|
|
108
|
-
const {
|
|
109
|
-
deltaX,
|
|
110
|
-
deltaY,
|
|
111
|
-
latestActivePointers,
|
|
112
|
-
startTime,
|
|
113
|
-
center,
|
|
114
|
-
event,
|
|
115
|
-
deltaPointersAngle,
|
|
116
|
-
} = data
|
|
117
|
-
const events = latestActivePointers ?? []
|
|
118
|
-
|
|
119
|
-
// Calculate the change in time
|
|
120
|
-
const now = Date.now()
|
|
121
|
-
const delay = now - startTime
|
|
122
|
-
|
|
123
|
-
const { velocityX, velocityY } = calculateVelocity(delay, deltaX, deltaY)
|
|
124
|
-
const pointersAverageDistance =
|
|
125
|
-
calculateAverageDistance(latestActivePointers)
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
deltaX,
|
|
129
|
-
deltaY,
|
|
130
|
-
velocityX,
|
|
131
|
-
velocityY,
|
|
132
|
-
delay,
|
|
133
|
-
center,
|
|
134
|
-
pointers: events,
|
|
135
|
-
startTime,
|
|
136
|
-
event,
|
|
137
|
-
deltaPointersAngle,
|
|
138
|
-
pointersAverageDistance,
|
|
139
|
-
} satisfies RecognizerEvent
|
|
140
|
-
}),
|
|
141
|
-
)
|
|
142
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Observable,
|
|
3
|
-
combineLatest,
|
|
4
|
-
distinctUntilChanged,
|
|
5
|
-
filter,
|
|
6
|
-
first,
|
|
7
|
-
map,
|
|
8
|
-
merge,
|
|
9
|
-
mergeMap,
|
|
10
|
-
scan,
|
|
11
|
-
switchMap,
|
|
12
|
-
takeUntil,
|
|
13
|
-
} from "rxjs"
|
|
14
|
-
import { Recognizer } from "../recognizer/Recognizer"
|
|
15
|
-
import { PanEvent, PanRecognizer } from "../pan/PanRecognizer"
|
|
16
|
-
import { RecognizerEvent } from "../recognizer/RecognizerEvent"
|
|
17
|
-
|
|
18
|
-
export interface RotateEvent extends RecognizerEvent {
|
|
19
|
-
type: "rotate" | "rotateStart" | "rotateEnd"
|
|
20
|
-
/**
|
|
21
|
-
* Current rotation angle
|
|
22
|
-
*/
|
|
23
|
-
angle: number
|
|
24
|
-
/**
|
|
25
|
-
* Delta angle between events
|
|
26
|
-
*/
|
|
27
|
-
deltaAngle: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type Options = {
|
|
31
|
-
posThreshold?: number
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export class RotateRecognizer extends Recognizer<Options> {
|
|
35
|
-
public events$: Observable<RotateEvent>
|
|
36
|
-
|
|
37
|
-
constructor(protected options: Options = {}) {
|
|
38
|
-
super(options)
|
|
39
|
-
|
|
40
|
-
this.events$ = this.config$.pipe(
|
|
41
|
-
switchMap((initializedWith) => {
|
|
42
|
-
const { posThreshold } = initializedWith.options ?? {}
|
|
43
|
-
|
|
44
|
-
const panRecognizer = new PanRecognizer({ posThreshold })
|
|
45
|
-
|
|
46
|
-
panRecognizer.initialize(initializedWith)
|
|
47
|
-
|
|
48
|
-
const hasLessThanTwoFinger$ = panRecognizer.events$.pipe(
|
|
49
|
-
filter(({ pointers }) => pointers.length < 2),
|
|
50
|
-
distinctUntilChanged(),
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
const hasMoreThanOneFinger$ = panRecognizer.events$.pipe(
|
|
54
|
-
map((event) => [event, event.pointers.length > 1] as const),
|
|
55
|
-
distinctUntilChanged(
|
|
56
|
-
(
|
|
57
|
-
[_, previousHasMoreThanOneFinger],
|
|
58
|
-
[__, currentHasMoreThanOneFinger],
|
|
59
|
-
) => previousHasMoreThanOneFinger === currentHasMoreThanOneFinger,
|
|
60
|
-
),
|
|
61
|
-
filter(([_, hasMoreThanOneFinger]) => hasMoreThanOneFinger),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
const start$ = hasMoreThanOneFinger$.pipe(
|
|
65
|
-
map(([event]) => ({
|
|
66
|
-
...event,
|
|
67
|
-
type: "rotateStart" as const,
|
|
68
|
-
angle: 0,
|
|
69
|
-
deltaAngle: 0,
|
|
70
|
-
})),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
const rotate$ = start$.pipe(
|
|
74
|
-
mergeMap(() =>
|
|
75
|
-
panRecognizer.events$.pipe(
|
|
76
|
-
scan<
|
|
77
|
-
PanEvent,
|
|
78
|
-
RotateEvent,
|
|
79
|
-
Pick<RotateEvent, "angle" | "deltaAngle">
|
|
80
|
-
>(
|
|
81
|
-
(acc, current) => {
|
|
82
|
-
const angle = acc.angle + current.deltaPointersAngle
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
...acc,
|
|
86
|
-
...current,
|
|
87
|
-
type: "rotate" as const,
|
|
88
|
-
angle,
|
|
89
|
-
deltaAngle: current.deltaPointersAngle,
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
{ angle: 0, deltaAngle: 0 },
|
|
93
|
-
),
|
|
94
|
-
takeUntil(hasLessThanTwoFinger$),
|
|
95
|
-
),
|
|
96
|
-
),
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
const end$ = start$.pipe(
|
|
100
|
-
mergeMap(() =>
|
|
101
|
-
combineLatest([rotate$, hasLessThanTwoFinger$] as const).pipe(
|
|
102
|
-
first(),
|
|
103
|
-
map(([event]) => ({
|
|
104
|
-
...event,
|
|
105
|
-
type: "rotateEnd" as const,
|
|
106
|
-
})),
|
|
107
|
-
),
|
|
108
|
-
),
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return merge(start$, rotate$, end$)
|
|
112
|
-
}),
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest"
|
|
2
|
-
import { buffer, first, lastValueFrom, tap, timer } from "rxjs"
|
|
3
|
-
import { SwipeRecognizer } from "./SwipeRecognizer"
|
|
4
|
-
|
|
5
|
-
const waitFor = (time: number) =>
|
|
6
|
-
new Promise((resolve) => setTimeout(resolve, time))
|
|
7
|
-
|
|
8
|
-
describe("SwipeRecognizer", () => {
|
|
9
|
-
let container = document.createElement("div")
|
|
10
|
-
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
container = document.createElement("div")
|
|
13
|
-
document.body.appendChild(container)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
function createPointerEvent({
|
|
17
|
-
type,
|
|
18
|
-
x,
|
|
19
|
-
y,
|
|
20
|
-
identifier,
|
|
21
|
-
}: {
|
|
22
|
-
type: string
|
|
23
|
-
x: number
|
|
24
|
-
y: number
|
|
25
|
-
identifier: number
|
|
26
|
-
}) {
|
|
27
|
-
const event = new PointerEvent(type, {
|
|
28
|
-
clientX: x,
|
|
29
|
-
clientY: y,
|
|
30
|
-
pointerId: identifier,
|
|
31
|
-
pointerType: "touch",
|
|
32
|
-
bubbles: true,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
event.x = x
|
|
37
|
-
// @ts-ignore
|
|
38
|
-
event.y = y
|
|
39
|
-
|
|
40
|
-
return event
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function sendPointerEvent({
|
|
44
|
-
type,
|
|
45
|
-
x,
|
|
46
|
-
y,
|
|
47
|
-
container,
|
|
48
|
-
identifier,
|
|
49
|
-
}: {
|
|
50
|
-
container: HTMLElement
|
|
51
|
-
type: string
|
|
52
|
-
x: number
|
|
53
|
-
y: number
|
|
54
|
-
identifier: number
|
|
55
|
-
}) {
|
|
56
|
-
const event = createPointerEvent({
|
|
57
|
-
type,
|
|
58
|
-
x,
|
|
59
|
-
y,
|
|
60
|
-
identifier,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
sendPointerEventFromEvent({ event, container })
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function sendPointerEventFromEvent({
|
|
67
|
-
event,
|
|
68
|
-
container,
|
|
69
|
-
}: {
|
|
70
|
-
event: PointerEvent
|
|
71
|
-
container: HTMLElement
|
|
72
|
-
}) {
|
|
73
|
-
container.dispatchEvent(event)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ~0.8 velocityX
|
|
77
|
-
const createVelocityOfOnePanMoving = async () => {
|
|
78
|
-
await waitFor(1)
|
|
79
|
-
|
|
80
|
-
sendPointerEvent({
|
|
81
|
-
container,
|
|
82
|
-
identifier: 1,
|
|
83
|
-
type: "pointerdown",
|
|
84
|
-
x: 0,
|
|
85
|
-
y: 0,
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
await waitFor(10)
|
|
89
|
-
|
|
90
|
-
sendPointerEvent({
|
|
91
|
-
container,
|
|
92
|
-
identifier: 1,
|
|
93
|
-
type: "pointermove",
|
|
94
|
-
x: 10,
|
|
95
|
-
y: 0,
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
await waitFor(10)
|
|
99
|
-
|
|
100
|
-
sendPointerEvent({
|
|
101
|
-
container,
|
|
102
|
-
identifier: 1,
|
|
103
|
-
type: "pointerup",
|
|
104
|
-
x: 20,
|
|
105
|
-
y: 0,
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
it("should detect a swipe if above threshold", async () => {
|
|
110
|
-
const waitLongEnough$ = timer(100)
|
|
111
|
-
const recognizer = new SwipeRecognizer({
|
|
112
|
-
escapeVelocity: 0.4,
|
|
113
|
-
posThreshold: 5
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
recognizer.initialize({
|
|
117
|
-
container,
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
const values = await lastValueFrom(
|
|
121
|
-
recognizer.events$.pipe(
|
|
122
|
-
tap({
|
|
123
|
-
subscribe: async () => {
|
|
124
|
-
await createVelocityOfOnePanMoving()
|
|
125
|
-
},
|
|
126
|
-
}),
|
|
127
|
-
buffer(waitLongEnough$),
|
|
128
|
-
first(),
|
|
129
|
-
),
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
expect(values).toMatchObject([{ type: "swipe" }])
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it("should not detect a swipe if below threshold", async () => {
|
|
136
|
-
const waitLongEnough$ = timer(100)
|
|
137
|
-
const recognizer = new SwipeRecognizer({
|
|
138
|
-
escapeVelocity: 10,
|
|
139
|
-
posThreshold: 5
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
recognizer.initialize({
|
|
143
|
-
container,
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
const values = await lastValueFrom(
|
|
147
|
-
recognizer.events$.pipe(
|
|
148
|
-
tap({
|
|
149
|
-
subscribe: async () => {
|
|
150
|
-
await createVelocityOfOnePanMoving()
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
buffer(waitLongEnough$),
|
|
154
|
-
first(),
|
|
155
|
-
),
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
expect(values).toMatchObject([])
|
|
159
|
-
})
|
|
160
|
-
})
|