Skip to content

Commit 96dac99

Browse files
committed
Fix targetTouches/changedTouches mismatch and multitouch bugs.
Fixes #159, #118
1 parent 91dd49f commit 96dac99

File tree

3 files changed

+34
-22
lines changed

3 files changed

+34
-22
lines changed

lib/DraggableCore.es6

+18-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22
import React, {PropTypes} from 'react';
3-
import {matchesSelector, addEvent, removeEvent, addUserSelectStyles,
3+
import {matchesSelector, addEvent, removeEvent, addUserSelectStyles, getTouchIdentifier,
44
removeUserSelectStyles, styleHacks} from './utils/domFns';
55
import {createCoreData, getControlPosition, snapToGrid} from './utils/positionFns';
66
import {dontSetMe} from './utils/shims';
@@ -29,7 +29,7 @@ type CoreState = {
2929
dragging: boolean,
3030
lastX: number,
3131
lastY: number,
32-
touchIdentifier: number
32+
touchIdentifier: ?number
3333
};
3434

3535
//
@@ -164,7 +164,7 @@ export default class DraggableCore extends React.Component {
164164
dragging: false,
165165
// Used while dragging to determine deltas.
166166
lastX: NaN, lastY: NaN,
167-
touchIdentifier: NaN
167+
touchIdentifier: null
168168
};
169169

170170
componentWillUnmount() {
@@ -195,12 +195,13 @@ export default class DraggableCore extends React.Component {
195195
// Set touch identifier in component state if this is a touch event. This allows us to
196196
// distinguish between individual touches on multitouch screens by identifying which
197197
// touchpoint was set to this element.
198-
if (e.targetTouches){
199-
this.setState({touchIdentifier: e.targetTouches[0].identifier});
200-
}
198+
const touchIdentifier = getTouchIdentifier(e);
199+
this.setState({touchIdentifier});
201200

202201
// Get the current drag point from the event. This is used as the offset.
203-
const {x, y} = getControlPosition(e, this);
202+
const position = getControlPosition(e, touchIdentifier, this);
203+
if (position == null) return; // not possible but satisfies flow
204+
const {x, y} = position;
204205

205206
// Create an event object with all the data parents need to make a decision here.
206207
const coreEvent = createCoreData(this, x, y);
@@ -234,12 +235,15 @@ export default class DraggableCore extends React.Component {
234235
};
235236

236237
handleDrag: EventHandler<MouseEvent> = (e) => {
237-
// Return if this is a touch event, but not the correct one for this element
238-
if (e.targetTouches && (e.targetTouches[0].identifier !== this.state.touchIdentifier)) return;
239238

240-
let {x, y} = getControlPosition(e, this);
239+
// Get the current drag point from the event. This is used as the offset.
240+
const position = getControlPosition(e, this.state.touchIdentifier, this);
241+
if (position == null) return;
242+
let {x, y} = position;
241243

242244
// Snap to grid if prop has been provided
245+
if (x !== x) debugger;
246+
243247
if (Array.isArray(this.props.grid)) {
244248
let deltaX = x - this.state.lastX, deltaY = y - this.state.lastY;
245249
[deltaX, deltaY] = snapToGrid(this.props.grid, deltaX, deltaY);
@@ -267,16 +271,14 @@ export default class DraggableCore extends React.Component {
267271
handleDragStop: EventHandler<MouseEvent> = (e) => {
268272
if (!this.state.dragging) return;
269273

270-
// Short circuit if this is not the correct touch event. `changedTouches` contains all
271-
// touch points that have been removed from the surface.
272-
if (e.changedTouches && (e.changedTouches[0].identifier !== this.state.touchIdentifier)) return;
274+
const position = getControlPosition(e, this.state.touchIdentifier, this);
275+
if (position == null) return;
276+
const {x, y} = position;
277+
const coreEvent = createCoreData(this, x, y);
273278

274279
// Remove user-select hack
275280
if (this.props.enableUserSelectHack) removeUserSelectStyles();
276281

277-
const {x, y} = getControlPosition(e, this);
278-
const coreEvent = createCoreData(this, x, y);
279-
280282
log('DraggableCore: handleDragStop: %j', coreEvent);
281283

282284
// Reset the el.

lib/utils/domFns.es6

+11-3
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ export function innerWidth(node: HTMLElement): number {
8383
}
8484

8585
// Get from offsetParent
86-
export function offsetXYFromParentOf(e: MouseEvent, node: HTMLElement & {offsetParent: HTMLElement}): ControlPosition {
87-
const evt = e.targetTouches ? e.targetTouches[0] : e;
88-
86+
export function offsetXYFromParentOf(evt: {clientX: number, clientY: number}, node: HTMLElement & {offsetParent: HTMLElement}): ControlPosition {
8987
const offsetParent = node.offsetParent || document.body;
9088
const offsetParentRect = node.offsetParent === document.body ? {left: 0, top: 0} : offsetParent.getBoundingClientRect();
9189

@@ -104,6 +102,16 @@ export function createSVGTransform({x, y}: {x: number, y: number}): string {
104102
return 'translate(' + x + ',' + y + ')';
105103
}
106104

105+
export function getTouch(e: MouseEvent, identifier: number): ?{clientX: number, clientY: number} {
106+
return (e.targetTouches && findInArray(e.targetTouches, t => identifier === t.identifier)) ||
107+
(e.changedTouches && findInArray(e.changedTouches, t => identifier === t.identifier));
108+
}
109+
110+
export function getTouchIdentifier(e: MouseEvent): ?number {
111+
if (e.targetTouches && e.targetTouches[0]) return e.targetTouches[0].identifier;
112+
if (e.changedTouches && e.changedTouches[0]) return e.changedTouches[0].identifier;
113+
}
114+
107115
// User-select Hacks:
108116
//
109117
// Useful for preventing blue highlights all over everything when dragging.

lib/utils/positionFns.es6

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @flow
22
import {isNum, int} from './shims';
33
import ReactDOM from 'react-dom';
4-
import {innerWidth, innerHeight, offsetXYFromParentOf, outerWidth, outerHeight} from './domFns';
4+
import {getTouch, innerWidth, innerHeight, offsetXYFromParentOf, outerWidth, outerHeight} from './domFns';
55

66
import type Draggable from '../Draggable';
77
import type {Bounds, ControlPosition, DraggableData} from './types';
@@ -63,8 +63,10 @@ export function canDragY(draggable: Draggable): boolean {
6363
}
6464

6565
// Get {x, y} positions from event.
66-
export function getControlPosition(e: MouseEvent, draggableCore: DraggableCore): ControlPosition {
67-
return offsetXYFromParentOf(e, ReactDOM.findDOMNode(draggableCore));
66+
export function getControlPosition(e: MouseEvent, touchIdentifier: ?number, draggableCore: DraggableCore): ?ControlPosition {
67+
const touchObj = typeof touchIdentifier === 'number' ? getTouch(e, touchIdentifier) : null;
68+
if (typeof touchIdentifier === 'number' && !touchObj) return null; // not the right touch
69+
return offsetXYFromParentOf(touchObj || e, ReactDOM.findDOMNode(draggableCore));
6870
}
6971

7072
// Create an data object exposed by <DraggableCore>'s events

0 commit comments

Comments
 (0)