diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 83b25178a..b3a3d03d1 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -17,7 +17,7 @@ var _lastSpace = null; export function behaviorDraw(context) { var dispatch = d3_dispatch( - 'move', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish' + 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish' ); var keybinding = utilKeybinding('draw'); @@ -32,8 +32,9 @@ export function behaviorDraw(context) { var _tolerance = 12; var _mouseLeave = false; var _lastMouse = null; + var _lastPointerUpEvent; - var _downPointerId; + var _downPointer; // use pointer events on supported platforms; fallback to mouse events var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; @@ -59,79 +60,81 @@ export function behaviorDraw(context) { return (d && d.properties && d.properties.target) ? d : {}; } - var _lastPointerUpEvent; - function pointerdown() { - if (_downPointerId) return; - var _downPointerId = d3_event.pointerId || 'mouse'; + if (_downPointer) return; var pointerLocGetter = utilFastMouse(this); + _downPointer = { + id: d3_event.pointerId || 'mouse', + pointerLocGetter: pointerLocGetter, + downTime: +new Date(), + downLoc: pointerLocGetter(d3_event) + }; - var element = d3_select(this); - var t1 = +new Date(); - var p1 = pointerLocGetter(d3_event); + dispatch.call('pointerdown', this, datum()); + } - element.on(_pointerPrefix + 'move.draw', null); + function pointerup() { - d3_select(window) - .on(_pointerPrefix + 'up.draw', pointerup, true); + if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return; - function pointerup() { + var downPointer = _downPointer; + _downPointer = null; - if (_downPointerId !== (d3_event.pointerId || 'mouse')) return; - _downPointerId = null; + _lastPointerUpEvent = d3_event; - _lastPointerUpEvent = d3_event; + if (downPointer.isCancelled) return; - element.on(_pointerPrefix + 'move.draw', pointermove); - d3_select(window).on(_pointerPrefix + 'up.draw', null); + var t2 = +new Date(); + var p2 = downPointer.pointerLocGetter(d3_event); + var dist = geoVecLength(downPointer.downLoc, p2); - var t2 = +new Date(); - var p2 = pointerLocGetter(d3_event); - var dist = geoVecLength(p1, p2); + if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) { + // Prevent a quick second click + d3_select(window).on('click.draw-block', function() { + d3_event.stopPropagation(); + }, true); - if (dist < _closeTolerance || (dist < _tolerance && (t2 - t1) < 500)) { - // Prevent a quick second click - d3_select(window).on('click.draw-block', function() { - d3_event.stopPropagation(); - }, true); + context.map().dblclickZoomEnable(false); - context.map().dblclickZoomEnable(false); + window.setTimeout(function() { + context.map().dblclickZoomEnable(true); + d3_select(window).on('click.draw-block', null); + }, 500); - window.setTimeout(function() { - context.map().dblclickZoomEnable(true); - d3_select(window).on('click.draw-block', null); - }, 500); - - click(p2); - } + click(p2); } } - function pointermove() { + if (_downPointer && !_downPointer.isCancelled) { + var p2 = _downPointer.pointerLocGetter(d3_event); + var dist = geoVecLength(_downPointer.downLoc, p2); + if (dist >= _closeTolerance) { + _downPointer.isCancelled = true; + dispatch.call('downcancel', this); + } + } + if ((d3_event.pointerType && d3_event.pointerType !== 'mouse') || d3_event.buttons || - _downPointerId) return; + _downPointer) return; // HACK: Mobile Safari likes to send one or more `mouse` type pointermove // events immediately after non-mouse pointerup events; detect and ignore them. if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && - geoVecLength([_lastPointerUpEvent.clientX, _lastPointerUpEvent.clientY], [d3_event.clientX, d3_event.clientY]) < 2 && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return; _lastMouse = d3_event; dispatch.call('move', this, datum()); } - function mouseenter() { _mouseLeave = false; } - function mouseleave() { _mouseLeave = true; } @@ -226,7 +229,7 @@ export function behaviorDraw(context) { context.install(_hover); context.install(_edit); - _downPointerId = null; + _downPointer = null; keybinding .on('⌫', backspace) @@ -242,6 +245,9 @@ export function behaviorDraw(context) { .on(_pointerPrefix + 'down.draw', pointerdown) .on(_pointerPrefix + 'move.draw', pointermove); + d3_select(window) + .on(_pointerPrefix + 'up.draw', pointerup, true); + d3_select(document) .call(keybinding); diff --git a/modules/behavior/draw_way.js b/modules/behavior/draw_way.js index 1c506cfb8..73d240e16 100644 --- a/modules/behavior/draw_way.js +++ b/modules/behavior/draw_way.js @@ -32,6 +32,8 @@ export function behaviorDrawWay(context, wayID, mode, startGraph) { var _headNodeID; var _annotation; + var _pointerHasMoved = false; + // The osmNode to be placed. // This is temporary and just follows the mouse cursor until an "add" event occurs. var _drawNode; @@ -244,6 +246,7 @@ export function behaviorDrawWay(context, wayID, mode, startGraph) { 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry ); + _pointerHasMoved = false; // Push an annotated state for undo to return back to. // We must make sure to replace or remove it later. @@ -255,7 +258,16 @@ export function behaviorDrawWay(context, wayID, mode, startGraph) { .initialNodeID(_headNodeID); behavior - .on('move', move) + .on('move', function() { + _pointerHasMoved = true; + move.apply(this, arguments); + }) + .on('down', function() { + move.apply(this, arguments); + }) + .on('downcancel', function() { + if (_drawNode) removeDrawNode(); + }) .on('click', drawWay.add) .on('clickWay', drawWay.addWay) .on('clickNode', drawWay.addNode) @@ -318,7 +330,6 @@ export function behaviorDrawWay(context, wayID, mode, startGraph) { function attemptAdd(d, loc, doAdd) { - var didJustAddDrawNode = false; if (_drawNode) { // move the node to the final loc in case move wasn't called // consistently (e.g. on touch devices) @@ -326,12 +337,11 @@ export function behaviorDrawWay(context, wayID, mode, startGraph) { _drawNode = context.entity(_drawNode.id); } else { createDrawNode(loc); - didJustAddDrawNode = true; } checkGeometry(true /* includeDrawNode */); if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) { - if (didJustAddDrawNode) { + if (!_pointerHasMoved) { // prevent the temporary draw node from appearing on touch devices removeDrawNode(); }