-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgesture-detector.js
120 lines (98 loc) · 4.06 KB
/
gesture-detector.js
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
// Component that detects and emits events for touch gestures
AFRAME.registerComponent('gesture-detector', {
schema: {
element: { default: '' },
},
init: function() {
this.targetElement = this.data.element && document.querySelector(this.data.element)
if (!this.targetElement) {
this.targetElement = this.el
}
this.internalState = {
previousState: null,
}
this.emitGestureEvent = this.emitGestureEvent.bind(this)
this.targetElement.addEventListener('touchstart', this.emitGestureEvent)
this.targetElement.addEventListener('touchend', this.emitGestureEvent)
this.targetElement.addEventListener('touchmove', this.emitGestureEvent)
},
remove: function() {
this.targetElement.removeEventListener('touchstart', this.emitGestureEvent)
this.targetElement.removeEventListener('touchend', this.emitGestureEvent)
this.targetElement.removeEventListener('touchmove', this.emitGestureEvent)
},
emitGestureEvent(event) {
const currentState = this.getTouchState(event)
const previousState = this.internalState.previousState
const gestureContinues = previousState &&
currentState &&
currentState.touchCount == previousState.touchCount
const gestureEnded = previousState && !gestureContinues
const gestureStarted = currentState && !gestureContinues
if (gestureEnded) {
const eventName = this.getEventPrefix(previousState.touchCount) + 'fingerend'
this.el.emit(eventName, previousState)
this.internalState.previousState = null
}
if (gestureStarted) {
currentState.startTime = performance.now()
currentState.startPosition = currentState.position
currentState.startSpread = currentState.spread
const eventName = this.getEventPrefix(currentState.touchCount) + 'fingerstart'
this.el.emit(eventName, currentState)
this.internalState.previousState = currentState
}
if (gestureContinues) {
const eventDetail = {
positionChange: {
x: currentState.position.x - previousState.position.x,
y: currentState.position.y - previousState.position.y
},
}
if (currentState.spread) {
eventDetail.spreadChange = currentState.spread - previousState.spread
}
// Update state with new data
Object.assign(previousState, currentState)
// Add state data to event detail
Object.assign(eventDetail, previousState)
const eventName = this.getEventPrefix(currentState.touchCount) + 'fingermove'
this.el.emit(eventName, eventDetail)
}
},
getTouchState: function(event) {
if (event.touches.length == 0) {
return null
}
// Convert event.touches to an array so we can use reduce
const touchList = []
for (let i = 0; i < event.touches.length; i++) {
touchList.push(event.touches[i])
}
const touchState = {
touchCount: touchList.length,
}
// Calculate center of all current touches
const centerPositionRawX = touchList.reduce((sum, touch) => sum + touch.clientX, 0) / touchList.length
const centerPositionRawY = touchList.reduce((sum, touch) => sum + touch.clientY, 0) / touchList.length
touchState.positionRaw = {x: centerPositionRawX, y: centerPositionRawY}
// Scale touch position and spread by average of window dimensions
const screenScale = 2 / (window.innerWidth + window.innerHeight)
touchState.position = {x: centerPositionRawX * screenScale, y: centerPositionRawY * screenScale}
// Calculate average spread of touches from the center point
if (touchList.length >= 2 ) {
const spread = touchList.reduce((sum, touch) => {
return sum +
Math.sqrt(
Math.pow(centerPositionRawX - touch.clientX, 2) +
Math.pow(centerPositionRawY - touch.clientY, 2))
}, 0) / touchList.length
touchState.spread = spread * screenScale
}
return touchState
},
getEventPrefix(touchCount) {
const numberNames = ['one', 'two', 'three', 'many']
return numberNames[Math.min(touchCount, 4) - 1]
}
})