@leafer-ui/interaction-web 1.0.0-rc.10
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/LICENSE +21 -0
- package/README.md +1 -0
- package/package.json +30 -0
- package/src/Interaction.ts +366 -0
- package/src/KeyEventHelper.ts +17 -0
- package/src/PointerEventHelper.ts +59 -0
- package/src/WheelEventHelper.ts +47 -0
- package/src/index.ts +1 -0
- package/types/index.d.ts +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present, Chao (Leafer) Wan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @leafer-ui/interaction-web
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leafer-ui/interaction-web",
|
|
3
|
+
"version": "1.0.0-rc.10",
|
|
4
|
+
"description": "@leafer-ui/interaction-web",
|
|
5
|
+
"author": "Chao (Leafer) Wan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "src/index.ts",
|
|
8
|
+
"types": "types/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"types",
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/leaferjs/ui.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/leaferjs/ui/tree/main/packages/interaction/interaction-web",
|
|
19
|
+
"bugs": "https://github.com/leaferjs/ui/issues",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"leafer-ui",
|
|
22
|
+
"leaferjs"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@leafer-ui/core": "1.0.0-rc.10"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@leafer/interface": "1.0.0-rc.10"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { IObject, IPointData, ITimer, IKeepTouchData, ICursorType } from '@leafer/interface'
|
|
2
|
+
import { MathHelper, InteractionBase, InteractionHelper, Cursor } from '@leafer-ui/core'
|
|
3
|
+
|
|
4
|
+
import { PointerEventHelper } from './PointerEventHelper'
|
|
5
|
+
import { WheelEventHelper } from './WheelEventHelper'
|
|
6
|
+
import { KeyEventHelper } from './KeyEventHelper'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
interface IClientPoint {
|
|
10
|
+
clientX: number
|
|
11
|
+
clientY: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface IGestureEvent extends IClientPoint, UIEvent {
|
|
15
|
+
scale: number
|
|
16
|
+
rotation: number
|
|
17
|
+
preventDefault(): void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const { getMoveEventData, getZoomEventData, getRotateEventData } = InteractionHelper
|
|
22
|
+
|
|
23
|
+
export class Interaction extends InteractionBase {
|
|
24
|
+
|
|
25
|
+
protected view: HTMLElement
|
|
26
|
+
|
|
27
|
+
protected viewEvents: IObject
|
|
28
|
+
protected windowEvents: IObject
|
|
29
|
+
|
|
30
|
+
protected usePointer: boolean
|
|
31
|
+
protected useMultiTouch: boolean
|
|
32
|
+
protected useTouch: boolean
|
|
33
|
+
|
|
34
|
+
protected touchTimer: ITimer
|
|
35
|
+
protected touches?: Touch[]
|
|
36
|
+
protected lastGestureScale: number
|
|
37
|
+
protected lastGestureRotation: number
|
|
38
|
+
|
|
39
|
+
protected __listenEvents(): void {
|
|
40
|
+
super.__listenEvents()
|
|
41
|
+
|
|
42
|
+
const view = this.view = this.canvas.view as HTMLCanvasElement
|
|
43
|
+
|
|
44
|
+
// PointerEvent > TouchEvent > MouseEvent
|
|
45
|
+
this.viewEvents = {
|
|
46
|
+
'pointerdown': this.onPointerDown,
|
|
47
|
+
'mousedown': this.onMouseDown,
|
|
48
|
+
'touchstart': this.onTouchStart,
|
|
49
|
+
|
|
50
|
+
'contextmenu': this.onContextMenu,
|
|
51
|
+
|
|
52
|
+
'wheel': this.onWheel,
|
|
53
|
+
'gesturestart': this.onGesturestart,
|
|
54
|
+
'gesturechange': this.onGesturechange,
|
|
55
|
+
'gestureend': this.onGestureend
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.windowEvents = {
|
|
59
|
+
'pointermove': this.onPointerMove,
|
|
60
|
+
'pointerup': this.onPointerUp,
|
|
61
|
+
'pointercancel': this.onPointerCancel,
|
|
62
|
+
|
|
63
|
+
'mousemove': this.onMouseMove,
|
|
64
|
+
'mouseup': this.onMouseUp,
|
|
65
|
+
|
|
66
|
+
// touch / multitouch
|
|
67
|
+
'touchmove': this.onTouchMove,
|
|
68
|
+
'touchend': this.onTouchEnd,
|
|
69
|
+
'touchcancel': this.onTouchCancel,
|
|
70
|
+
|
|
71
|
+
'keydown': this.onKeyDown,
|
|
72
|
+
'keyup': this.onKeyUp,
|
|
73
|
+
|
|
74
|
+
'scroll': this.onScroll
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { viewEvents, windowEvents } = this
|
|
78
|
+
|
|
79
|
+
for (let name in viewEvents) {
|
|
80
|
+
viewEvents[name] = viewEvents[name].bind(this)
|
|
81
|
+
view.addEventListener(name, viewEvents[name])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (let name in windowEvents) {
|
|
85
|
+
windowEvents[name] = windowEvents[name].bind(this)
|
|
86
|
+
window.addEventListener(name, windowEvents[name])
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected __removeListenEvents(): void {
|
|
91
|
+
super.__removeListenEvents()
|
|
92
|
+
|
|
93
|
+
const { viewEvents, windowEvents } = this
|
|
94
|
+
|
|
95
|
+
for (let name in viewEvents) {
|
|
96
|
+
this.view.removeEventListener(name, viewEvents[name])
|
|
97
|
+
this.viewEvents = {}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (let name in windowEvents) {
|
|
101
|
+
window.removeEventListener(name, windowEvents[name])
|
|
102
|
+
this.windowEvents = {}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
protected getLocal(p: IClientPoint, updateClient?: boolean): IPointData {
|
|
107
|
+
if (updateClient) this.canvas.updateClientBounds()
|
|
108
|
+
const { clientBounds } = this.canvas
|
|
109
|
+
return { x: p.clientX - clientBounds.x, y: p.clientY - clientBounds.y }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected getTouches(touches: TouchList): Touch[] {
|
|
113
|
+
const list: Touch[] = []
|
|
114
|
+
for (let i = 0, len = touches.length; i < len; i++) {
|
|
115
|
+
list.push(touches[i])
|
|
116
|
+
}
|
|
117
|
+
return list
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
protected preventDefaultPointer(e: UIEvent): void {
|
|
122
|
+
const { pointer } = this.config
|
|
123
|
+
if (pointer.preventDefault) e.preventDefault()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected preventDefaultWheel(e: UIEvent): void {
|
|
127
|
+
const { wheel } = this.config
|
|
128
|
+
if (wheel.preventDefault) e.preventDefault()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
protected preventWindowPointer(e: UIEvent) {
|
|
132
|
+
return !this.downData && e.target !== this.view
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// key
|
|
136
|
+
protected onKeyDown(e: KeyboardEvent): void {
|
|
137
|
+
this.keyDown(KeyEventHelper.convert(e))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
protected onKeyUp(e: KeyboardEvent): void {
|
|
141
|
+
this.keyUp(KeyEventHelper.convert(e))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// context menu
|
|
145
|
+
|
|
146
|
+
protected onContextMenu(e: PointerEvent): void {
|
|
147
|
+
if (this.config.pointer.preventDefaultMenu) e.preventDefault()
|
|
148
|
+
this.menu(PointerEventHelper.convert(e, this.getLocal(e)))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
protected onScroll(): void {
|
|
152
|
+
this.canvas.updateClientBounds()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// pointer
|
|
156
|
+
protected onPointerDown(e: PointerEvent): void {
|
|
157
|
+
this.preventDefaultPointer(e)
|
|
158
|
+
|
|
159
|
+
this.usePointer || (this.usePointer = true)
|
|
160
|
+
if (this.useMultiTouch) return
|
|
161
|
+
this.pointerDown(PointerEventHelper.convert(e, this.getLocal(e)))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
protected onPointerMove(e: PointerEvent): void {
|
|
165
|
+
this.usePointer || (this.usePointer = true)
|
|
166
|
+
if (this.useMultiTouch || this.preventWindowPointer(e)) return
|
|
167
|
+
this.pointerMove(PointerEventHelper.convert(e, this.getLocal(e, true)))
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected onPointerUp(e: PointerEvent): void {
|
|
171
|
+
if (this.downData) this.preventDefaultPointer(e)
|
|
172
|
+
if (this.useMultiTouch || this.preventWindowPointer(e)) return
|
|
173
|
+
this.pointerUp(PointerEventHelper.convert(e, this.getLocal(e)))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected onPointerCancel(): void {
|
|
177
|
+
if (this.useMultiTouch) return
|
|
178
|
+
this.pointerCancel()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
// mouse
|
|
183
|
+
protected onMouseDown(e: MouseEvent): void {
|
|
184
|
+
this.preventDefaultPointer(e)
|
|
185
|
+
|
|
186
|
+
if (this.useTouch || this.usePointer) return
|
|
187
|
+
this.pointerDown(PointerEventHelper.convertMouse(e, this.getLocal(e)))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected onMouseMove(e: MouseEvent): void {
|
|
191
|
+
if (this.useTouch || this.usePointer || this.preventWindowPointer(e)) return
|
|
192
|
+
this.pointerMove(PointerEventHelper.convertMouse(e, this.getLocal(e, true)))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
protected onMouseUp(e: MouseEvent): void {
|
|
196
|
+
if (this.downData) this.preventDefaultPointer(e)
|
|
197
|
+
if (this.useTouch || this.usePointer || this.preventWindowPointer(e)) return
|
|
198
|
+
this.pointerUp(PointerEventHelper.convertMouse(e, this.getLocal(e)))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
protected onMouseCancel(): void {
|
|
202
|
+
if (this.useTouch || this.usePointer) return
|
|
203
|
+
this.pointerCancel()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
// touch
|
|
208
|
+
protected onTouchStart(e: TouchEvent): void {
|
|
209
|
+
e.preventDefault()
|
|
210
|
+
|
|
211
|
+
this.multiTouchStart(e)
|
|
212
|
+
|
|
213
|
+
if (this.usePointer) return
|
|
214
|
+
if (this.touchTimer) {
|
|
215
|
+
window.clearTimeout(this.touchTimer)
|
|
216
|
+
this.touchTimer = 0
|
|
217
|
+
}
|
|
218
|
+
this.useTouch = true
|
|
219
|
+
const touch = PointerEventHelper.getTouch(e)
|
|
220
|
+
this.pointerDown(PointerEventHelper.convertTouch(e, this.getLocal(touch, true)))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected onTouchMove(e: TouchEvent): void {
|
|
224
|
+
this.multiTouchMove(e)
|
|
225
|
+
|
|
226
|
+
if (this.usePointer || this.preventWindowPointer(e)) return
|
|
227
|
+
const touch = PointerEventHelper.getTouch(e)
|
|
228
|
+
this.pointerMove(PointerEventHelper.convertTouch(e, this.getLocal(touch)))
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
protected onTouchEnd(e: TouchEvent): void {
|
|
232
|
+
this.multiTouchEnd()
|
|
233
|
+
|
|
234
|
+
if (this.usePointer || this.preventWindowPointer(e)) return
|
|
235
|
+
if (this.touchTimer) clearTimeout(this.touchTimer)
|
|
236
|
+
this.touchTimer = setTimeout(() => {
|
|
237
|
+
this.useTouch = false
|
|
238
|
+
}, 500) // stop touch > mouse
|
|
239
|
+
const touch = PointerEventHelper.getTouch(e)
|
|
240
|
+
this.pointerUp(PointerEventHelper.convertTouch(e, this.getLocal(touch)))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
protected onTouchCancel(): void {
|
|
244
|
+
if (this.usePointer) return
|
|
245
|
+
this.pointerCancel()
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
// multiTouch
|
|
250
|
+
protected multiTouchStart(e: TouchEvent): void {
|
|
251
|
+
this.useMultiTouch = (e.touches.length >= 2)
|
|
252
|
+
this.touches = this.useMultiTouch ? this.getTouches(e.touches) : undefined
|
|
253
|
+
if (this.useMultiTouch) this.pointerCancel()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected multiTouchMove(e: TouchEvent): void {
|
|
257
|
+
if (!this.useMultiTouch) return
|
|
258
|
+
if (e.touches.length > 1) {
|
|
259
|
+
const touches = this.getTouches(e.touches)
|
|
260
|
+
const list = this.getKeepTouchList(this.touches, touches)
|
|
261
|
+
if (list.length > 1) {
|
|
262
|
+
this.multiTouch(InteractionHelper.getBase(e), list)
|
|
263
|
+
this.touches = touches
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
protected multiTouchEnd(): void {
|
|
269
|
+
this.touches = null
|
|
270
|
+
this.useMultiTouch = false
|
|
271
|
+
this.transformEnd()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
protected getKeepTouchList(old: Touch[], touches: Touch[]): IKeepTouchData[] {
|
|
275
|
+
let to: Touch
|
|
276
|
+
const list: IKeepTouchData[] = []
|
|
277
|
+
old.forEach(from => {
|
|
278
|
+
to = touches.find(touch => touch.identifier === from.identifier)
|
|
279
|
+
if (to) list.push({ from: this.getLocal(from), to: this.getLocal(to) })
|
|
280
|
+
})
|
|
281
|
+
return list
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
protected getLocalTouchs(points: Touch[]): IPointData[] {
|
|
285
|
+
return points.map(point => this.getLocal(point))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
// wheel
|
|
290
|
+
protected onWheel(e: WheelEvent): void {
|
|
291
|
+
this.preventDefaultWheel(e)
|
|
292
|
+
|
|
293
|
+
const { wheel } = this.config
|
|
294
|
+
const scale = wheel.getScale ? wheel.getScale(e, wheel) : WheelEventHelper.getScale(e, wheel)
|
|
295
|
+
const local = this.getLocal(e)
|
|
296
|
+
|
|
297
|
+
const eventBase = InteractionHelper.getBase(e)
|
|
298
|
+
scale !== 1 ? this.zoom(getZoomEventData(local, scale, eventBase)) : this.move(getMoveEventData(local, wheel.getMove ? wheel.getMove(e, wheel) : WheelEventHelper.getMove(e, wheel), eventBase))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
// safari
|
|
303
|
+
protected onGesturestart(e: IGestureEvent): void {
|
|
304
|
+
this.preventDefaultWheel(e)
|
|
305
|
+
|
|
306
|
+
this.lastGestureScale = 1
|
|
307
|
+
this.lastGestureRotation = 0
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
protected onGesturechange(e: IGestureEvent): void {
|
|
311
|
+
this.preventDefaultWheel(e)
|
|
312
|
+
|
|
313
|
+
const local = this.getLocal(e)
|
|
314
|
+
const eventBase = InteractionHelper.getBase(e)
|
|
315
|
+
const changeScale = e.scale / this.lastGestureScale
|
|
316
|
+
const changeAngle = e.rotation - this.lastGestureRotation
|
|
317
|
+
|
|
318
|
+
let { rotateSpeed } = this.config.wheel
|
|
319
|
+
rotateSpeed = MathHelper.within(rotateSpeed, 0, 1)
|
|
320
|
+
|
|
321
|
+
this.zoom(getZoomEventData(local, changeScale * changeScale, eventBase))
|
|
322
|
+
this.rotate(getRotateEventData(local, changeAngle / Math.PI * 180 * (rotateSpeed / 4 + 0.1), eventBase))
|
|
323
|
+
|
|
324
|
+
this.lastGestureScale = e.scale
|
|
325
|
+
this.lastGestureRotation = e.rotation
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
protected onGestureend(e: IGestureEvent): void {
|
|
329
|
+
this.preventDefaultWheel(e)
|
|
330
|
+
|
|
331
|
+
this.transformEnd()
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
// cursor
|
|
336
|
+
public setCursor(cursor: ICursorType | ICursorType[]): void {
|
|
337
|
+
super.setCursor(cursor)
|
|
338
|
+
const list: ICursorType[] = []
|
|
339
|
+
this.eachCursor(cursor, list)
|
|
340
|
+
if (typeof list[list.length - 1] === 'object') list.push('default')
|
|
341
|
+
this.canvas.view.style.cursor = list.map(item => (typeof item === 'object') ? `url(${item.url}) ${item.x || 0} ${item.y || 0}` : item).join(',')
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
protected eachCursor(cursor: ICursorType | ICursorType[], list: ICursorType[], level = 0): void {
|
|
345
|
+
level++
|
|
346
|
+
if (cursor instanceof Array) {
|
|
347
|
+
cursor.forEach(item => this.eachCursor(item, list, level))
|
|
348
|
+
} else {
|
|
349
|
+
const custom = typeof cursor === 'string' && Cursor.get(cursor)
|
|
350
|
+
if (custom && level < 2) {
|
|
351
|
+
this.eachCursor(custom, list, level)
|
|
352
|
+
} else {
|
|
353
|
+
list.push(cursor)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public destroy(): void {
|
|
359
|
+
if (this.view) {
|
|
360
|
+
super.destroy()
|
|
361
|
+
this.view = null
|
|
362
|
+
this.touches = null
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IKeyEvent } from '@leafer/interface'
|
|
2
|
+
import { InteractionHelper } from '@leafer-ui/core'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const KeyEventHelper = {
|
|
6
|
+
|
|
7
|
+
convert(e: KeyboardEvent): IKeyEvent {
|
|
8
|
+
const base = InteractionHelper.getBase(e)
|
|
9
|
+
const data: IKeyEvent = {
|
|
10
|
+
...base,
|
|
11
|
+
code: e.code,
|
|
12
|
+
key: e.key
|
|
13
|
+
}
|
|
14
|
+
return data
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { IPointData, IPointerEvent, PointerType } from '@leafer/interface'
|
|
2
|
+
import { InteractionHelper } from '@leafer-ui/core'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const PointerEventHelper = {
|
|
6
|
+
|
|
7
|
+
convert(e: PointerEvent, local: IPointData): IPointerEvent {
|
|
8
|
+
const base = InteractionHelper.getBase(e)
|
|
9
|
+
const data: IPointerEvent = {
|
|
10
|
+
...base,
|
|
11
|
+
x: local.x,
|
|
12
|
+
y: local.y,
|
|
13
|
+
width: e.width,
|
|
14
|
+
height: e.height,
|
|
15
|
+
pointerType: e.pointerType as PointerType,
|
|
16
|
+
pressure: e.pressure,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (data.pointerType === 'pen') {
|
|
20
|
+
data.tangentialPressure = e.tangentialPressure
|
|
21
|
+
data.tiltX = e.tiltX
|
|
22
|
+
data.tiltY = e.tiltY
|
|
23
|
+
data.twist = e.twist
|
|
24
|
+
}
|
|
25
|
+
return data
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
convertMouse(e: MouseEvent, local: IPointData): IPointerEvent {
|
|
29
|
+
const base = InteractionHelper.getBase(e)
|
|
30
|
+
return {
|
|
31
|
+
...base,
|
|
32
|
+
x: local.x,
|
|
33
|
+
y: local.y,
|
|
34
|
+
width: 1,
|
|
35
|
+
height: 1,
|
|
36
|
+
pointerType: 'mouse',
|
|
37
|
+
pressure: 0.5,
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
convertTouch(e: TouchEvent, local: IPointData): IPointerEvent {
|
|
42
|
+
const touch = PointerEventHelper.getTouch(e)
|
|
43
|
+
const base = InteractionHelper.getBase(e)
|
|
44
|
+
return {
|
|
45
|
+
...base,
|
|
46
|
+
x: local.x,
|
|
47
|
+
y: local.y,
|
|
48
|
+
width: 1,
|
|
49
|
+
height: 1,
|
|
50
|
+
pointerType: 'touch',
|
|
51
|
+
pressure: touch.force,
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
getTouch(e: TouchEvent): Touch {
|
|
56
|
+
return e.targetTouches[0] || e.changedTouches[0]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IPointData, IWheelConfig } from '@leafer/interface'
|
|
2
|
+
import { MathHelper, Platform } from '@leafer-ui/core'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const WheelEventHelper = {
|
|
6
|
+
|
|
7
|
+
getMove(e: WheelEvent, config: IWheelConfig): IPointData {
|
|
8
|
+
let { moveSpeed } = config
|
|
9
|
+
let { deltaX, deltaY } = e
|
|
10
|
+
if (e.shiftKey && !deltaX) { // Window
|
|
11
|
+
deltaX = deltaY
|
|
12
|
+
deltaY = 0
|
|
13
|
+
}
|
|
14
|
+
if (deltaX > 50) deltaX = Math.max(50, deltaX / 3)
|
|
15
|
+
if (deltaY > 50) deltaY = Math.max(50, deltaY / 3)
|
|
16
|
+
return { x: -deltaX * moveSpeed * 2, y: -deltaY * moveSpeed * 2 }
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
getScale(e: WheelEvent, config: IWheelConfig): number {
|
|
20
|
+
|
|
21
|
+
let zoom: boolean
|
|
22
|
+
let scale = 1
|
|
23
|
+
let { zoomMode, zoomSpeed } = config
|
|
24
|
+
|
|
25
|
+
const delta = e.deltaY || e.deltaX
|
|
26
|
+
|
|
27
|
+
if (zoomMode) {
|
|
28
|
+
// mac 触摸板滚动手势的deltaY是整数, 鼠标滚动/触摸板缩放的deltaY有小数点, firfox鼠标滚动为整数,为18或19的倍数
|
|
29
|
+
// windows 始终是整数
|
|
30
|
+
zoom = (zoomMode === 'mouse') ? true : (!e.deltaX && (Platform.intWheelDeltaY ? Math.abs(delta) > 17 : Math.ceil(delta) !== delta))
|
|
31
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) zoom = true
|
|
32
|
+
} else {
|
|
33
|
+
zoom = !e.shiftKey && (e.metaKey || e.ctrlKey)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (zoom) {
|
|
37
|
+
zoomSpeed = MathHelper.within(zoomSpeed, 0, 1)
|
|
38
|
+
const min = e.deltaY ? config.delta.y : config.delta.x
|
|
39
|
+
scale = 1 - delta / (min * 4) * zoomSpeed // zoomSpeed
|
|
40
|
+
if (scale < 0.5) scale = 0.5
|
|
41
|
+
if (scale >= 1.5) scale = 1.5
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return scale
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Interaction } from './Interaction'
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { IObject, ITimer, IPointData, IKeepTouchData, ICursorType } from '@leafer/interface';
|
|
2
|
+
import { InteractionBase } from '@leafer-ui/core';
|
|
3
|
+
|
|
4
|
+
interface IClientPoint {
|
|
5
|
+
clientX: number;
|
|
6
|
+
clientY: number;
|
|
7
|
+
}
|
|
8
|
+
interface IGestureEvent extends IClientPoint, UIEvent {
|
|
9
|
+
scale: number;
|
|
10
|
+
rotation: number;
|
|
11
|
+
preventDefault(): void;
|
|
12
|
+
}
|
|
13
|
+
declare class Interaction extends InteractionBase {
|
|
14
|
+
protected view: HTMLElement;
|
|
15
|
+
protected viewEvents: IObject;
|
|
16
|
+
protected windowEvents: IObject;
|
|
17
|
+
protected usePointer: boolean;
|
|
18
|
+
protected useMultiTouch: boolean;
|
|
19
|
+
protected useTouch: boolean;
|
|
20
|
+
protected touchTimer: ITimer;
|
|
21
|
+
protected touches?: Touch[];
|
|
22
|
+
protected lastGestureScale: number;
|
|
23
|
+
protected lastGestureRotation: number;
|
|
24
|
+
protected __listenEvents(): void;
|
|
25
|
+
protected __removeListenEvents(): void;
|
|
26
|
+
protected getLocal(p: IClientPoint, updateClient?: boolean): IPointData;
|
|
27
|
+
protected getTouches(touches: TouchList): Touch[];
|
|
28
|
+
protected preventDefaultPointer(e: UIEvent): void;
|
|
29
|
+
protected preventDefaultWheel(e: UIEvent): void;
|
|
30
|
+
protected preventWindowPointer(e: UIEvent): boolean;
|
|
31
|
+
protected onKeyDown(e: KeyboardEvent): void;
|
|
32
|
+
protected onKeyUp(e: KeyboardEvent): void;
|
|
33
|
+
protected onContextMenu(e: PointerEvent): void;
|
|
34
|
+
protected onScroll(): void;
|
|
35
|
+
protected onPointerDown(e: PointerEvent): void;
|
|
36
|
+
protected onPointerMove(e: PointerEvent): void;
|
|
37
|
+
protected onPointerUp(e: PointerEvent): void;
|
|
38
|
+
protected onPointerCancel(): void;
|
|
39
|
+
protected onMouseDown(e: MouseEvent): void;
|
|
40
|
+
protected onMouseMove(e: MouseEvent): void;
|
|
41
|
+
protected onMouseUp(e: MouseEvent): void;
|
|
42
|
+
protected onMouseCancel(): void;
|
|
43
|
+
protected onTouchStart(e: TouchEvent): void;
|
|
44
|
+
protected onTouchMove(e: TouchEvent): void;
|
|
45
|
+
protected onTouchEnd(e: TouchEvent): void;
|
|
46
|
+
protected onTouchCancel(): void;
|
|
47
|
+
protected multiTouchStart(e: TouchEvent): void;
|
|
48
|
+
protected multiTouchMove(e: TouchEvent): void;
|
|
49
|
+
protected multiTouchEnd(): void;
|
|
50
|
+
protected getKeepTouchList(old: Touch[], touches: Touch[]): IKeepTouchData[];
|
|
51
|
+
protected getLocalTouchs(points: Touch[]): IPointData[];
|
|
52
|
+
protected onWheel(e: WheelEvent): void;
|
|
53
|
+
protected onGesturestart(e: IGestureEvent): void;
|
|
54
|
+
protected onGesturechange(e: IGestureEvent): void;
|
|
55
|
+
protected onGestureend(e: IGestureEvent): void;
|
|
56
|
+
setCursor(cursor: ICursorType | ICursorType[]): void;
|
|
57
|
+
protected eachCursor(cursor: ICursorType | ICursorType[], list: ICursorType[], level?: number): void;
|
|
58
|
+
destroy(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Interaction };
|