A lot of fine-tuning and fixing to better handle touch scrolling.

Relates to #686
This commit is contained in:
Jürg Lehni 2016-01-14 10:59:14 +01:00
parent d5f2ff479d
commit 1a6bf972d5
2 changed files with 42 additions and 27 deletions

View file

@ -286,7 +286,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
* least one event handler was called and none of the called handlers * least one event handler was called and none of the called handlers
* wants to enforce the default. * wants to enforce the default.
*/ */
_handleEvent: function(type, event, point) { _handleEvent: function(type, event, point, mouse) {
// Update global reference to this scope. // Update global reference to this scope.
paper = this._scope; paper = this._scope;
var minDistance = this.minDistance, var minDistance = this.minDistance,
@ -303,11 +303,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
matchMaxDistance = false, matchMaxDistance = false,
called = false, // Has at least one handler been called? called = false, // Has at least one handler been called?
enforced = false, // Does a handler want to enforce the default? enforced = false, // Does a handler want to enforce the default?
tool = this, tool = this;
mouse = {};
// Create a simple lookup object to quickly check for different
// mouse event types.
mouse[type.substr(5)] = true;
function update(start, minDistance, maxDistance) { function update(start, minDistance, maxDistance) {
var toolPoint = tool._point, var toolPoint = tool._point,

View file

@ -698,7 +698,8 @@ new function() { // Injection scope for mouse events on the browser
var prevFocus, var prevFocus,
tempFocus, tempFocus,
mouseDown = false; dragging = false, // mousedown that started on a view.
mouseDown = false; // mouesdown anywhere.
function getView(event) { function getView(event) {
// Get the view from the current event target. // Get the view from the current event target.
@ -782,7 +783,7 @@ new function() { // Injection scope for mouse events on the browser
// Get the view from the event, and store a reference to the view that // Get the view from the event, and store a reference to the view that
// should receive keyboard input. // should receive keyboard input.
var view = View._focused = getView(event); var view = View._focused = getView(event);
mouseDown = true; dragging = true;
view._handleEvent('mousedown', event); view._handleEvent('mousedown', event);
}; };
@ -792,13 +793,12 @@ new function() { // Injection scope for mouse events on the browser
// See if we can get the view from the current event target, and // See if we can get the view from the current event target, and
// handle the mouse move over it. // handle the mouse move over it.
var target = getView(event); var target = getView(event);
if (target) { if (target && view !== target) {
// Temporarily focus this view without making it sticky, so Key // Temporarily focus this view without making it sticky, so Key
// events are handled too during the mouse over. // events are handled too during the mouse over.
// If we switch view, fire one last mousemove in the old view, // As we switch view, fire one last mousemove in the old view,
// to give items the change to receive a mouseleave, etc. // to give items the change to receive a mouseleave, etc.
if (view !== target) handleMouseMove(view, event);
handleMouseMove(view, event);
prevFocus = view; prevFocus = view;
view = View._focused = tempFocus = target; view = View._focused = tempFocus = target;
} else if (tempFocus && tempFocus === view) { } else if (tempFocus && tempFocus === view) {
@ -811,11 +811,18 @@ new function() { // Injection scope for mouse events on the browser
handleMouseMove(view, event); handleMouseMove(view, event);
}; };
docEvents[mousedown] = function(event) {
// In order to not switch views during scroll dragging on touch devices,
// we need to know if the mouse was clicked anywhere on the document
// (see docEvents[mousemove]) The rest happens in viewEvents[mousedown].
mouseDown = true;
};
docEvents[mouseup] = function(event) { docEvents[mouseup] = function(event) {
var view = View._focused; var view = View._focused;
if (view && mouseDown) if (view && dragging)
view._handleEvent('mouseup', event); view._handleEvent('mouseup', event);
mouseDown = false; mouseDown = dragging = false;
}; };
DomEvent.add(document, docEvents); DomEvent.add(document, docEvents);
@ -938,7 +945,8 @@ new function() { // Injection scope for mouse events on the browser
clickItem, clickItem,
clickTime, clickTime,
dblClick, dblClick,
overView; overView,
wasInView = false;
return { return {
_viewEvents: viewEvents, _viewEvents: viewEvents,
@ -954,7 +962,7 @@ new function() { // Injection scope for mouse events on the browser
// it to a mousedrag. // it to a mousedrag.
// NOTE: emitEvent(), as well as Tool#_handleEvent() fall back to // NOTE: emitEvent(), as well as Tool#_handleEvent() fall back to
// mousemove if the objects don't respond to mousedrag. // mousemove if the objects don't respond to mousedrag.
if (type === 'mousemove' && mouseDown) if (type === 'mousemove' && dragging)
type = 'mousedrag'; type = 'mousedrag';
// Before handling events, process removeOn() calls for cleanup. // Before handling events, process removeOn() calls for cleanup.
if (project) if (project)
@ -963,14 +971,16 @@ new function() { // Injection scope for mouse events on the browser
point = this.getEventPoint(event); point = this.getEventPoint(event);
// Run the hit-test on items first, but only if we're required to do // Run the hit-test on items first, but only if we're required to do
// so for this given mouse event, see #_countItemEvent(). // so for this given mouse event, see #_countItemEvent().
var hit = handleItems && this._project.hitTest(point, { var inView = this.getBounds().contains(point),
hit = inView && handleItems && this._project.hitTest(point, {
tolerance: 0, tolerance: 0,
fill: true, fill: true,
stroke: true stroke: true
}), }),
item = hit && hit.item || undefined, item = hit && hit.item || undefined,
inView = this.getBounds().contains(point), // Keep track if view event should be handled, so we can use it
wasInView = this === overView, // to decide if tool._handleEvent() shall be called after.
handle = false,
stopped = false, stopped = false,
mouse = {}; mouse = {};
// Create a simple lookup object to quickly check for different // Create a simple lookup object to quickly check for different
@ -981,9 +991,8 @@ new function() { // Injection scope for mouse events on the browser
// Handle mousemove first, even if this is not actually a mousemove // Handle mousemove first, even if this is not actually a mousemove
// event but the mouse has moved since the last event, but do not // event but the mouse has moved since the last event, but do not
// allow it to stop the other events in that case. // allow it to stop the other events in that case.
var nativeMove = mouse.move || mouse.drag; var nativeMove = mouse.move || mouse.drag,
moveType = nativeMove && type moveType = nativeMove && type || 'mousemove';
|| lastPoint && !lastPoint.equals(point) && 'mousemove';
if (moveType) { if (moveType) {
// Handle mouseenter / leave between items, as well as views. // Handle mouseenter / leave between items, as well as views.
if (item !== overItem) { if (item !== overItem) {
@ -997,14 +1006,25 @@ new function() { // Injection scope for mouse events on the browser
emitEvent(this, inView ? 'mouseenter' : 'mouseleave', event, emitEvent(this, inView ? 'mouseenter' : 'mouseleave', event,
point); point);
overView = inView ? this : null; overView = inView ? this : null;
handle = nativeMove; // To include the leaving move.
} }
if (inView || mouse.drag && !lastPoint.equals(point)) if (inView || mouse.drag && !lastPoint.equals(point)) {
stopped = emitEvents(this, item, moveType, event, point, stopped = emitEvents(this, item, moveType, event, point,
lastPoint); lastPoint);
handle = nativeMove;
}
wasInView = inView;
} }
if (!nativeMove) { // Now handle mousedown / mouseup
// Now handle mousedown / mouseup if (!nativeMove &&
// We emit mousedown only when in the view, and mouseup
// regardless, as long as the mousedown event was inside.
(handle = mouse.down && inView || mouse.up && !!downPoint)) {
stopped = emitEvents(this, item, type, event, point, downPoint); stopped = emitEvents(this, item, type, event, point, downPoint);
// Clear wasInView so we're not accidentally handling mousedrag
// events that started outside the view as mousemove events on
// the view (needed to handle touch scrolling correctly).
wasInView = false;
if (mouse.down) { if (mouse.down) {
// See if we're clicking again on the same item, within the // See if we're clicking again on the same item, within the
// double-click time. Firefox uses 300ms as the max time // double-click time. Firefox uses 300ms as the max time
@ -1034,8 +1054,7 @@ new function() { // Injection scope for mouse events on the browser
// to only be fired if we're inside the view or if we just left it. // to only be fired if we're inside the view or if we just left it.
// Prevent default if at least one handler was called, and none of // Prevent default if at least one handler was called, and none of
// them enforces default, to prevent scrolling on touch devices. // them enforces default, to prevent scrolling on touch devices.
if (tool && (mouse.move || inView || wasInView) if (handle && tool && tool._handleEvent(type, event, point, mouse)
&& tool._handleEvent(type, event, point)
|| called && !enforced) || called && !enforced)
event.preventDefault(); event.preventDefault();
// In the end we always call update(), which only updates the view // In the end we always call update(), which only updates the view