-
Notifications
You must be signed in to change notification settings - Fork 803
Expand file tree
/
Copy pathplugin.ts
More file actions
206 lines (171 loc) · 6.03 KB
/
Copy pathplugin.ts
File metadata and controls
206 lines (171 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import type { Interactable } from '@interactjs/core/Interactable'
import type { InteractEvent, EventPhase } from '@interactjs/core/InteractEvent'
import type { Interaction, DoPhaseArg } from '@interactjs/core/Interaction'
import type { PerActionDefaults } from '@interactjs/core/options'
import type { Scope, Plugin } from '@interactjs/core/scope'
import type { Rect, PointerType, ListenersArg, OrBoolean } from '@interactjs/core/types'
import is from '@interactjs/utils/is'
import * as pointerUtils from '@interactjs/utils/pointerUtils'
declare module '@interactjs/core/Interaction' {
interface Interaction {
gesture?: {
angle: number // angle from first to second touch
distance: number
scale: number // gesture.distance / gesture.startDistance
startAngle: number // angle of line joining two touches
startDistance: number // distance between two touches of touchStart
}
}
}
declare module '@interactjs/core/Interactable' {
interface Interactable {
gesturable(options: Partial<OrBoolean<GesturableOptions>> | boolean): this
gesturable(): GesturableOptions
/**
* ```js
* interact(element).gesturable({
* onstart: function (event) {},
* onmove : function (event) {},
* onend : function (event) {},
*
* // limit multiple gestures.
* // See the explanation in {@link Interactable.draggable} example
* max: Infinity,
* maxPerElement: 1,
* })
*
* var isGestureable = interact(element).gesturable()
* ```
*
* Gets or sets whether multitouch gestures can be performed on the target
*
* @param options - true/false or An object with event listeners to be fired on gesture events (makes the Interactable gesturable)
* @returns A boolean indicating if this can be the target of gesture events, or this Interactable
*/
gesturable(options?: Partial<OrBoolean<GesturableOptions>> | boolean): this | GesturableOptions
}
}
declare module '@interactjs/core/options' {
interface ActionDefaults {
gesture: GesturableOptions
}
}
declare module '@interactjs/core/types' {
interface ActionMap {
gesture?: typeof gesture
}
}
export interface GesturableOptions extends PerActionDefaults {
onstart?: ListenersArg
onmove?: ListenersArg
onend?: ListenersArg
}
export interface GestureEvent extends InteractEvent<'gesture'> {
distance: number
angle: number
da: number // angle change
scale: number // ratio of distance start to current event
ds: number // scale change
box: Rect // enclosing box of all points
touches: PointerType[]
}
export interface GestureSignalArg extends DoPhaseArg<'gesture', EventPhase> {
iEvent: GestureEvent
interaction: Interaction<'gesture'>
}
function install(scope: Scope) {
const { actions, Interactable, defaults } = scope
Interactable.prototype.gesturable = function (
this: InstanceType<typeof Interactable>,
options: GesturableOptions | boolean,
) {
if (is.object(options)) {
this.options.gesture.enabled = options.enabled !== false
this.setPerAction('gesture', options)
this.setOnEvents('gesture', options)
return this
}
if (is.bool(options)) {
this.options.gesture.enabled = options
return this
}
return this.options.gesture as GesturableOptions
} as Interactable['gesturable']
actions.map.gesture = gesture
actions.methodDict.gesture = 'gesturable'
defaults.actions.gesture = gesture.defaults
}
function updateGestureProps({ interaction, iEvent, phase }: GestureSignalArg) {
if (interaction.prepared.name !== 'gesture') return
const pointers = interaction.pointers.map((p) => p.pointer)
const starting = phase === 'start'
const ending = phase === 'end'
const deltaSource = interaction.interactable.options.deltaSource
iEvent.touches = [pointers[0], pointers[1]]
if (starting) {
iEvent.distance = pointerUtils.touchDistance(pointers, deltaSource)
iEvent.box = pointerUtils.touchBBox(pointers)
iEvent.scale = 1
iEvent.ds = 0
iEvent.angle = pointerUtils.touchAngle(pointers, deltaSource)
iEvent.da = 0
interaction.gesture.startDistance = iEvent.distance
interaction.gesture.startAngle = iEvent.angle
} else if (ending || interaction.pointers.length < 2) {
const prevEvent = interaction.prevEvent as GestureEvent
iEvent.distance = prevEvent.distance
iEvent.box = prevEvent.box
iEvent.scale = prevEvent.scale
iEvent.ds = 0
iEvent.angle = prevEvent.angle
iEvent.da = 0
} else {
iEvent.distance = pointerUtils.touchDistance(pointers, deltaSource)
iEvent.box = pointerUtils.touchBBox(pointers)
iEvent.scale = iEvent.distance / interaction.gesture.startDistance
iEvent.angle = pointerUtils.touchAngle(pointers, deltaSource)
iEvent.ds = iEvent.scale - interaction.gesture.scale
iEvent.da = iEvent.angle - interaction.gesture.angle
}
interaction.gesture.distance = iEvent.distance
interaction.gesture.angle = iEvent.angle
if (is.number(iEvent.scale) && iEvent.scale !== Infinity && !isNaN(iEvent.scale)) {
interaction.gesture.scale = iEvent.scale
}
}
const gesture: Plugin = {
id: 'actions/gesture',
before: ['actions/drag', 'actions/resize'],
install,
listeners: {
'interactions:action-start': updateGestureProps,
'interactions:action-move': updateGestureProps,
'interactions:action-end': updateGestureProps,
'interactions:new': ({ interaction }) => {
interaction.gesture = {
angle: 0,
distance: 0,
scale: 1,
startAngle: 0,
startDistance: 0,
}
},
'auto-start:check': (arg) => {
if (arg.interaction.pointers.length < 2) {
return undefined
}
const gestureOptions = arg.interactable.options.gesture
if (!(gestureOptions && gestureOptions.enabled)) {
return undefined
}
arg.action = { name: 'gesture' }
return false
},
},
defaults: {},
getCursor() {
return ''
},
filterEventType: (type: string) => type.search('gesture') === 0,
}
export default gesture