[fix] ScrollView scrolling

Scrolling is broken by the patch that adds ResponderEvent support for
multi-input devices: 6a9212df40

By calling 'preventDefault' on every touch event, scroll events were
cancelled. This patch shifts the responsibility for calling
'preventDefault' to the 'View' event handler normalizer, and only on
touch events within the Responder system.

Fix #175
This commit is contained in:
Nicolas Gallagher
2016-07-29 14:00:35 -07:00
parent 8201906703
commit d54a84701a
4 changed files with 27 additions and 16 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ const ScrollView = React.createClass({
_handleContentOnLayout(e: Object) { _handleContentOnLayout(e: Object) {
const { width, height } = e.nativeEvent.layout const { width, height } = e.nativeEvent.layout
this.props.onContentSizeChange && this.props.onContentSizeChange(width, height) this.props.onContentSizeChange(width, height)
}, },
render() { render() {
+14 -5
View File
@@ -105,7 +105,7 @@ class View extends Component {
const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => { const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => {
const handler = this.props[handlerName] const handler = this.props[handlerName]
if (typeof handler === 'function') { if (typeof handler === 'function') {
handlerProps[handlerName] = this._normalizeEventForHandler(handler) handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName)
} }
return handlerProps return handlerProps
}, {}) }, {})
@@ -125,12 +125,21 @@ class View extends Component {
return createReactDOMComponent(props) return createReactDOMComponent(props)
} }
_normalizeEventForHandler(handler) { _normalizeEventForHandler(handler, handlerName) {
const callback = (e) => { // Browsers fire mouse events after touch events. This causes the
// ResponderEvents and their handlers to fire twice for Touchables.
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
// events.
const shouldCancelEvent = handlerName.indexOf('onResponder') === 0
return (e) => {
e.nativeEvent = normalizeNativeEvent(e.nativeEvent) e.nativeEvent = normalizeNativeEvent(e.nativeEvent)
return handler(e) const returnValue = handler(e)
if (shouldCancelEvent && e.cancelable) {
e.preventDefault()
}
return returnValue
} }
return callback
} }
} }
@@ -45,12 +45,6 @@ ResponderTouchHistoryStore.recordTouchTrack = (topLevelType, nativeEvent) => {
if ((topLevelType === topMouseMove) && !ResponderTouchHistoryStore.touchHistory.touchBank.length) { if ((topLevelType === topMouseMove) && !ResponderTouchHistoryStore.touchHistory.touchBank.length) {
return return
} }
// Cancel mouse events that browsers fire after touch events
if (topLevelType === topTouchStart || topLevelType === topTouchMove || topLevelType === topTouchEnd) {
if (nativeEvent.target.getAttribute('href') !== undefined) {
nativeEvent.preventDefault()
}
}
const normalizedEvent = normalizeNativeEvent(nativeEvent) const normalizedEvent = normalizeNativeEvent(nativeEvent)
originalRecordTouchTrack.call(ResponderTouchHistoryStore, topLevelType, normalizedEvent) originalRecordTouchTrack.call(ResponderTouchHistoryStore, topLevelType, normalizedEvent)
+12 -4
View File
@@ -7,6 +7,7 @@ const normalizeTouches = (touches = []) => Array.prototype.slice.call(touches).m
const locationY = touch.pageY - rect.top const locationY = touch.pageY - rect.top
return { return {
_normalized: true,
clientX: touch.clientX, clientX: touch.clientX,
clientY: touch.clientY, clientY: touch.clientY,
force: touch.force, force: touch.force,
@@ -32,10 +33,13 @@ function normalizeTouchEvent(nativeEvent) {
const touches = normalizeTouches(nativeEvent.touches) const touches = normalizeTouches(nativeEvent.touches)
const event = { const event = {
_normalized: true,
changedTouches, changedTouches,
originalEvent: nativeEvent,
pageX: nativeEvent.pageX, pageX: nativeEvent.pageX,
pageY: nativeEvent.pageY, pageY: nativeEvent.pageY,
preventDefault: nativeEvent.preventDefault.bind(nativeEvent),
stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent),
stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent),
target: nativeEvent.target, target: nativeEvent.target,
// normalize the timestamp // normalize the timestamp
// https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events // https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events
@@ -56,6 +60,7 @@ function normalizeTouchEvent(nativeEvent) {
function normalizeMouseEvent(nativeEvent) { function normalizeMouseEvent(nativeEvent) {
const touches = [{ const touches = [{
_normalized: true,
clientX: nativeEvent.clientX, clientX: nativeEvent.clientX,
clientY: nativeEvent.clientY, clientY: nativeEvent.clientY,
force: nativeEvent.force, force: nativeEvent.force,
@@ -70,13 +75,16 @@ function normalizeMouseEvent(nativeEvent) {
timestamp: Date.now() timestamp: Date.now()
}] }]
return { return {
_normalized: true,
changedTouches: touches, changedTouches: touches,
identifier: touches[0].identifier, identifier: touches[0].identifier,
locationX: nativeEvent.offsetX, locationX: nativeEvent.offsetX,
locationY: nativeEvent.offsetY, locationY: nativeEvent.offsetY,
originalEvent: nativeEvent,
pageX: nativeEvent.pageX, pageX: nativeEvent.pageX,
pageY: nativeEvent.pageY, pageY: nativeEvent.pageY,
preventDefault: nativeEvent.preventDefault.bind(nativeEvent),
stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent),
stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent),
target: nativeEvent.target, target: nativeEvent.target,
timestamp: touches[0].timestamp, timestamp: touches[0].timestamp,
touches: (nativeEvent.type === 'mouseup') ? [] : touches touches: (nativeEvent.type === 'mouseup') ? [] : touches
@@ -84,8 +92,8 @@ function normalizeMouseEvent(nativeEvent) {
} }
function normalizeNativeEvent(nativeEvent) { function normalizeNativeEvent(nativeEvent) {
if (nativeEvent.originalEvent) { return nativeEvent } if (nativeEvent._normalized) { return nativeEvent }
const eventType = nativeEvent.type || (nativeEvent.originalEvent && nativeEvent.originalEvent.type) || '' const eventType = nativeEvent.type || ''
const mouse = eventType.indexOf('mouse') >= 0 const mouse = eventType.indexOf('mouse') >= 0
return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent) return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent)
} }