Further streamline and improve tool mouse-event handling.

This commit is contained in:
Jürg Lehni 2016-01-14 14:16:20 +01:00
parent e2723f0312
commit cf924512c0
3 changed files with 51 additions and 65 deletions

View file

@ -119,7 +119,7 @@ var Segment = Base.extend(/** @lends Segment# */{
// Nothing
} else if (count === 1) {
// NOTE: This copies from existing segments through accessors.
if ('point' in arg0) {
if (arg0 && 'point' in arg0) {
point = arg0.point;
handleIn = arg0.handleIn;
handleOut = arg0.handleOut;

View file

@ -55,7 +55,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
PaperScopeItem.call(this);
this._firstMove = true;
this._count = 0;
this._downCount = 0;
this._downCount = -1; // So first is 0
this._set(props);
},
@ -278,7 +278,6 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
* }
*/
/**
* Private method to handle tool-events.
*
@ -287,28 +286,30 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
_handleEvent: function(type, event, point, mouse) {
// Update global reference to this scope.
paper = this._scope;
var minDistance = this.minDistance,
// If there is no mousedrag event installed, fall back to mousemove,
// with which we share the actual event handling code anyhow.
var move = mouse.move || mouse.drag && !this.responds(type);
// Make sure type is not 'mousedrag' if we fell back.
if (move)
type = 'mousemove';
var responds = this.responds(type),
minDistance = this.minDistance,
maxDistance = this.maxDistance,
// In order for idleInterval drag events to work, we need to not
// check the first call for a change of position. Subsequent calls
// required by min/maxDistance functionality will require it,
// otherwise this might loop endlessly.
needsChange = false,
// If the mouse is moving faster than maxDistance, do not produce
// events for what is left after the first event is generated in
// case it is shorter than maxDistance, as this would produce weird
// results. matchMaxDistance controls this.
matchMaxDistance = false,
called = false,
tool = this;
// Updates the internal tool state, taking into account minDistance and
// maxDistance and interpolating "fake" events along the moved distance
// to respect their settings, if necessary.
// Returns true as long as events should be fired, false when the target
// is reached.
function update(start, minDistance, maxDistance) {
var toolPoint = tool._point,
pt = point;
if (start) {
tool._count = 0;
} else {
tool._count++;
if (pt.equals(toolPoint))
return false;
if (minDistance != null || maxDistance != null) {
var vector = pt.subtract(toolPoint),
distance = vector.getLength();
@ -317,63 +318,48 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
// Produce a new point on the way to point if point is
// further away than maxDistance
if (maxDistance) {
if (distance > maxDistance) {
pt = toolPoint.add(vector.normalize(maxDistance));
} else if (matchMaxDistance) {
return false;
}
pt = toolPoint.add(vector.normalize(
Math.min(distance, maxDistance)));
}
}
if (needsChange && pt.equals(toolPoint))
return false;
tool._count++;
}
// Make sure mousemove events have lastPoint set even for the first
// move so event.delta is always defined for them.
// TODO: Decide whether mousedown also should always have delta set.
tool._lastPoint = start && mouse.move ? pt : toolPoint;
tool._point = pt;
if (responds) {
tool._point = pt;
tool._lastPoint = move || mouse.drag
// Make sure mousemove events have lastPoint set even for
// the first move so event.delta is always defined for them.
? start && move ? pt : toolPoint
// Set lastPoint to previous downPoint, or current point if
// this is the first mousedown, so there's always a delta.
// This way mouseup has a delta spanning over the full drag.
: tool._downPoint || pt;
}
// Keep track of downPoint regardless of the value of response
if (mouse.down) {
tool._lastPoint = tool._downPoint;
tool._downPoint = pt;
tool._downCount++;
} else if (mouse.up) {
// Mouse up events return the down point for last point, so
// delta is spanning over the whole drag.
tool._lastPoint = tool._downPoint;
}
return true;
}
function emit() {
called = tool.responds(type)
&& tool.emit(type, new ToolEvent(tool, type, event))
|| called;
function emit(firstMove) {
if (responds) {
called = tool.emit(type, new ToolEvent(tool, type, event))
|| called;
tool._firstMove = firstMove;
}
}
if (mouse.down) {
update(true);
emit();
update(responds);
emit(false);
} else if (mouse.up) {
update(false, null, maxDistance);
emit();
// Start with new values for 'mousemove'
update(true);
this._firstMove = true;
} else {
// If there is no mousedrag event installed, fall back to mousemove,
// with which we share the actual event handling code anyhow.
var drag = mouse.drag && this.responds(type);
if (!drag)
type = 'mousemove';
needsChange = !drag;
while (update(!drag && this._firstMove, minDistance, maxDistance)) {
emit();
if (drag) {
needsChange = matchMaxDistance = true;
} else {
this._firstMove = false;
}
}
emit(true);
} else if (responds) {
while (update(this._firstMove, minDistance, maxDistance))
emit(false);
}
return called;
}

View file

@ -1018,12 +1018,12 @@ new function() { // Injection scope for mouse events on the browser
emitEvent(this, inView ? 'mouseenter' : 'mouseleave', event,
point);
overView = inView ? this : null;
handle = nativeMove; // To include the leaving move.
handle = true; // To include the leaving move.
}
if (inView || mouse.drag && !lastPoint.equals(point)) {
stopped = emitEvents(this, item, moveType, event, point,
lastPoint);
handle = nativeMove;
handle = true;
}
wasInView = inView;
}
@ -1031,12 +1031,8 @@ new function() { // Injection scope for mouse events on the browser
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)) {
(handle = mouse.down && inView || mouse.up && 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) {
// See if we're clicking again on the same item, within the
// double-click time. Firefox uses 300ms as the max time
@ -1060,6 +1056,10 @@ new function() { // Injection scope for mouse events on the browser
}
downItem = dragItem = null;
}
// 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;
}
lastPoint = point;
// Now finally call the tool events, but filter mouse move events