mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-22 14:21:44 +00:00
[fix] PanResponder improvements + mouse support
- Adds `locationX` and `locationY` to touch events - Adds `timestamp` to the `touches` and `touchesChanged` data - Add mouse event support Close #94
This commit is contained in:
committed by
Nicolas Gallagher
parent
6d7d98c149
commit
f6f8d30aba
@@ -6,6 +6,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
import normalizeNativeEvent from './normalizeNativeEvent';
|
||||
var TouchHistoryMath = require('./TouchHistoryMath');
|
||||
|
||||
var currentCentroidXOfTouchesChangedAfter =
|
||||
@@ -287,21 +288,26 @@ var PanResponder = {
|
||||
var panHandlers = {
|
||||
onStartShouldSetResponder: function(e) {
|
||||
return config.onStartShouldSetPanResponder === undefined ? false :
|
||||
config.onStartShouldSetPanResponder(e, gestureState);
|
||||
config.onStartShouldSetPanResponder(normalizeEvent(e), gestureState);
|
||||
},
|
||||
onMoveShouldSetResponder: function(e) {
|
||||
return config.onMoveShouldSetPanResponder === undefined ? false :
|
||||
config.onMoveShouldSetPanResponder(e, gestureState);
|
||||
config.onMoveShouldSetPanResponder(normalizeEvent(e), gestureState);
|
||||
},
|
||||
onStartShouldSetResponderCapture: function(e) {
|
||||
// TODO: Actually, we should reinitialize the state any time
|
||||
// touches.length increases from 0 active to > 0 active.
|
||||
if (e.nativeEvent.touches.length === 1) {
|
||||
if (e.nativeEvent.touches) {
|
||||
if (e.nativeEvent.touches.length === 1) {
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
}
|
||||
}
|
||||
else if (e.nativeEvent.type === 'mousedown') {
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
}
|
||||
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
|
||||
return config.onStartShouldSetPanResponderCapture !== undefined ?
|
||||
config.onStartShouldSetPanResponderCapture(e, gestureState) : false;
|
||||
config.onStartShouldSetPanResponderCapture(normalizeEvent(e), gestureState) : false;
|
||||
},
|
||||
|
||||
onMoveShouldSetResponderCapture: function(e) {
|
||||
@@ -314,7 +320,7 @@ var PanResponder = {
|
||||
}
|
||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||
return config.onMoveShouldSetPanResponderCapture ?
|
||||
config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;
|
||||
config.onMoveShouldSetPanResponderCapture(normalizeEvent(e), gestureState) : false;
|
||||
},
|
||||
|
||||
onResponderGrant: function(e) {
|
||||
@@ -322,25 +328,25 @@ var PanResponder = {
|
||||
gestureState.y0 = currentCentroidY(e.touchHistory);
|
||||
gestureState.dx = 0;
|
||||
gestureState.dy = 0;
|
||||
config.onPanResponderGrant && config.onPanResponderGrant(e, gestureState);
|
||||
config.onPanResponderGrant && config.onPanResponderGrant(normalizeEvent(e), gestureState);
|
||||
// TODO: t7467124 investigate if this can be removed
|
||||
return config.onShouldBlockNativeResponder === undefined ? true :
|
||||
config.onShouldBlockNativeResponder();
|
||||
},
|
||||
|
||||
onResponderReject: function(e) {
|
||||
config.onPanResponderReject && config.onPanResponderReject(e, gestureState);
|
||||
config.onPanResponderReject && config.onPanResponderReject(normalizeEvent(e), gestureState);
|
||||
},
|
||||
|
||||
onResponderRelease: function(e) {
|
||||
config.onPanResponderRelease && config.onPanResponderRelease(e, gestureState);
|
||||
config.onPanResponderRelease && config.onPanResponderRelease(normalizeEvent(e), gestureState);
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
},
|
||||
|
||||
onResponderStart: function(e) {
|
||||
var touchHistory = e.touchHistory;
|
||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||
config.onPanResponderStart && config.onPanResponderStart(e, gestureState);
|
||||
config.onPanResponderStart && config.onPanResponderStart(normalizeEvent(e), gestureState);
|
||||
},
|
||||
|
||||
onResponderMove: function(e) {
|
||||
@@ -353,13 +359,13 @@ var PanResponder = {
|
||||
// Filter out any touch moves past the first one - we would have
|
||||
// already processed multi-touch geometry during the first event.
|
||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||
config.onPanResponderMove && config.onPanResponderMove(e, gestureState);
|
||||
config.onPanResponderMove && config.onPanResponderMove(normalizeEvent(e), gestureState);
|
||||
},
|
||||
|
||||
onResponderEnd: function(e) {
|
||||
var touchHistory = e.touchHistory;
|
||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||
config.onPanResponderEnd && config.onPanResponderEnd(e, gestureState);
|
||||
config.onPanResponderEnd && config.onPanResponderEnd(normalizeEvent(e), gestureState);
|
||||
},
|
||||
|
||||
onResponderTerminate: function(e) {
|
||||
@@ -370,11 +376,17 @@ var PanResponder = {
|
||||
|
||||
onResponderTerminationRequest: function(e) {
|
||||
return config.onPanResponderTerminationRequest === undefined ? true :
|
||||
config.onPanResponderTerminationRequest(e, gestureState);
|
||||
config.onPanResponderTerminationRequest(normalizeEvent(e), gestureState);
|
||||
},
|
||||
};
|
||||
return {panHandlers: panHandlers};
|
||||
},
|
||||
};
|
||||
|
||||
function normalizeEvent(e) {
|
||||
const normalizedEvent = Object.create(e);
|
||||
normalizedEvent.nativeEvent = normalizeNativeEvent(e.nativeEvent, e.type);
|
||||
return normalizedEvent;
|
||||
}
|
||||
|
||||
module.exports = PanResponder;
|
||||
|
||||
@@ -4,6 +4,7 @@ import EventConstants from 'react/lib/EventConstants'
|
||||
import EventPluginRegistry from 'react/lib/EventPluginRegistry'
|
||||
import ResponderEventPlugin from 'react/lib/ResponderEventPlugin'
|
||||
import ResponderTouchHistoryStore from 'react/lib/ResponderTouchHistoryStore'
|
||||
import normalizeNativeEvent from './normalizeNativeEvent'
|
||||
|
||||
const {
|
||||
topMouseDown,
|
||||
@@ -37,53 +38,13 @@ ResponderEventPlugin.eventTypes.selectionChangeShouldSetResponder.dependencies =
|
||||
ResponderEventPlugin.eventTypes.scrollShouldSetResponder.dependencies = [ topScroll ]
|
||||
ResponderEventPlugin.eventTypes.startShouldSetResponder.dependencies = startDependencies
|
||||
|
||||
// Mobile Safari re-uses touch objects, so we copy the properties we want and normalize the identifier
|
||||
const normalizeTouches = (touches = []) => Array.prototype.slice.call(touches).map((touch) => {
|
||||
const identifier = touch.identifier > 20 ? (touch.identifier % 20) : touch.identifier
|
||||
|
||||
return {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY,
|
||||
force: touch.force,
|
||||
identifier: identifier,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
radiusX: touch.radiusX,
|
||||
radiusY: touch.radiusY,
|
||||
rotationAngle: touch.rotationAngle,
|
||||
screenX: touch.screenX,
|
||||
screenY: touch.screenY,
|
||||
target: touch.target
|
||||
}
|
||||
})
|
||||
|
||||
const normalizeNativeEvent = (nativeEvent) => {
|
||||
const changedTouches = normalizeTouches(nativeEvent.changedTouches)
|
||||
const touches = normalizeTouches(nativeEvent.touches)
|
||||
|
||||
const event = {
|
||||
changedTouches,
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
target: nativeEvent.target,
|
||||
// normalize the timestamp
|
||||
// https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events
|
||||
timestamp: Date.now(),
|
||||
touches
|
||||
}
|
||||
|
||||
if (changedTouches[0]) {
|
||||
event.identifier = changedTouches[0].identifier
|
||||
event.pageX = changedTouches[0].pageX
|
||||
event.pageY = changedTouches[0].pageY
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
const originalRecordTouchTrack = ResponderTouchHistoryStore.recordTouchTrack
|
||||
|
||||
ResponderTouchHistoryStore.recordTouchTrack = (topLevelType, nativeEvent) => {
|
||||
// Filter out mouse-move events when the mouse button is not down
|
||||
if ((topLevelType === 'topMouseMove') && !ResponderTouchHistoryStore.touchHistory.touchBank.length) {
|
||||
return
|
||||
}
|
||||
originalRecordTouchTrack.call(ResponderTouchHistoryStore, topLevelType, normalizeNativeEvent(nativeEvent))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Mobile Safari re-uses touch objects, so we copy the properties we want and normalize the identifier
|
||||
const normalizeTouches = (touches = []) => Array.prototype.slice.call(touches).map((touch) => {
|
||||
const identifier = touch.identifier > 20 ? (touch.identifier % 20) : touch.identifier
|
||||
|
||||
return {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY,
|
||||
force: touch.force,
|
||||
identifier: identifier,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
radiusX: touch.radiusX,
|
||||
radiusY: touch.radiusY,
|
||||
rotationAngle: touch.rotationAngle,
|
||||
screenX: touch.screenX,
|
||||
screenY: touch.screenY,
|
||||
target: touch.target,
|
||||
// normalize the timestamp
|
||||
// https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events
|
||||
timestamp: Date.now()
|
||||
}
|
||||
})
|
||||
|
||||
function normalizeTouchEvent(nativeEvent) {
|
||||
const changedTouches = normalizeTouches(nativeEvent.changedTouches)
|
||||
const touches = normalizeTouches(nativeEvent.touches)
|
||||
|
||||
const event = {
|
||||
changedTouches,
|
||||
domEvent: nativeEvent,
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
target: nativeEvent.target,
|
||||
// normalize the timestamp
|
||||
// https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events
|
||||
timestamp: Date.now(),
|
||||
touches
|
||||
}
|
||||
|
||||
if (changedTouches[0]) {
|
||||
event.identifier = changedTouches[0].identifier
|
||||
event.pageX = changedTouches[0].pageX
|
||||
event.pageY = changedTouches[0].pageY
|
||||
const rect = changedTouches[0].target.getBoundingClientRect()
|
||||
event.locationX = changedTouches[0].pageX - rect.left
|
||||
event.locationY = changedTouches[0].pageY - rect.top
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
function normalizeMouseEvent(nativeEvent) {
|
||||
const touches = [{
|
||||
clientX: nativeEvent.clientX,
|
||||
clientY: nativeEvent.clientY,
|
||||
force: nativeEvent.force,
|
||||
identifier: 0,
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
screenX: nativeEvent.screenX,
|
||||
screenY: nativeEvent.screenY,
|
||||
target: nativeEvent.target,
|
||||
timestamp: nativeEvent.timestamp || Date.now()
|
||||
}]
|
||||
return {
|
||||
changedTouches: touches,
|
||||
domEvent: nativeEvent,
|
||||
identifier: touches[0].identifier,
|
||||
locationX: nativeEvent.offsetX,
|
||||
locationY: nativeEvent.offsetY,
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
target: nativeEvent.target,
|
||||
timestamp: touches[0].timestamp,
|
||||
touches: (nativeEvent.type === 'mouseup') ? [] : touches
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeNativeEvent(nativeEvent) {
|
||||
const mouse = nativeEvent.type.indexOf('mouse') >= 0
|
||||
return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent)
|
||||
}
|
||||
|
||||
export default normalizeNativeEvent
|
||||
Reference in New Issue
Block a user