Skip to content

Commit

Permalink
Differently support multi mouse events (fabricjs#5785)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored Jul 14, 2019
1 parent 5b641c0 commit ef98a58
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 289 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
},
"optionalDependencies": {
"canvas": "^2.6.0",
"jsdom": "15.1.0"
"jsdom": "^15.1.0"
},
"devDependencies": {
"eslint": "4.18.x",
Expand Down
16 changes: 13 additions & 3 deletions src/brushes/pencil_brush.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
* Inovoked on mouse down
* @param {Object} pointer
*/
onMouseDown: function(pointer) {
onMouseDown: function(pointer, options) {
if (!this.canvas._isMainEvent(options.e)) {
return;
}
this._prepareForDrawing(pointer);
// capture coordinates immediately
// this allows to draw dots (when movement never occurs)
Expand All @@ -49,7 +52,10 @@
* Inovoked on mouse move
* @param {Object} pointer
*/
onMouseMove: function(pointer) {
onMouseMove: function(pointer, options) {
if (!this.canvas._isMainEvent(options.e)) {
return;
}
if (this._captureDrawingPath(pointer) && this._points.length > 1) {
if (this.needsFullRender()) {
// redraw curve
Expand All @@ -75,9 +81,13 @@
/**
* Invoked on mouse up
*/
onMouseUp: function() {
onMouseUp: function(options) {
if (!this.canvas._isMainEvent(options.e)) {
return true;
}
this.oldEnd = undefined;
this._finalizeAndAddPath();
return false;
},

/**
Expand Down
8 changes: 2 additions & 6 deletions src/canvas.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,8 @@
height: height + 'px',
left: 0,
top: 0,
'touch-action': this.allowTouchScrolling ? 'manipulation' : 'none'
'touch-action': this.allowTouchScrolling ? 'manipulation' : 'none',
'-ms-touch-action': this.allowTouchScrolling ? 'manipulation' : 'none'
});
element.width = width;
element.height = height;
Expand Down Expand Up @@ -1720,9 +1721,4 @@
fabric.Canvas[prop] = fabric.StaticCanvas[prop];
}
}

if (fabric.isTouchSupported) {
/** @ignore */
fabric.Canvas.prototype._setCursorFromEvent = function() { };
}
})();
163 changes: 125 additions & 38 deletions src/mixins/canvas_events.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
addEventOptions = { passive: false };

function checkClick(e, value) {
return 'which' in e ? e.which === value : e.button === value - 1;
return e.button && (e.button === value - 1);
}

fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
Expand All @@ -36,6 +36,13 @@
'nw-resize'
],

/**
* Contains the id of the touch event that owns the fabric transform
* @type Number
* @private
*/
mainTouchId: null,

/**
* Adds mouse listeners to canvas
* @private
Expand All @@ -49,9 +56,17 @@
this.addOrRemove(addListener, 'add');
},

/**
* return an event prefix pointer or mouse.
* @private
*/
_getEventPrefix: function () {
return this.enablePointerEvents ? 'pointer' : 'mouse';
},

addOrRemove: function(functor, eventjsFunctor) {
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this.enablePointerEvents ? 'pointer' : 'mouse';
eventTypePrefix = this._getEventPrefix();
functor(fabric.window, 'resize', this._onResize);
functor(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
functor(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
Expand All @@ -60,12 +75,13 @@
functor(canvasElement, 'wheel', this._onMouseWheel);
functor(canvasElement, 'contextmenu', this._onContextMenu);
functor(canvasElement, 'dblclick', this._onDoubleClick);
functor(canvasElement, 'touchstart', this._onMouseDown, addEventOptions);
functor(canvasElement, 'touchmove', this._onMouseMove, addEventOptions);
functor(canvasElement, 'dragover', this._onDragOver);
functor(canvasElement, 'dragenter', this._onDragEnter);
functor(canvasElement, 'dragleave', this._onDragLeave);
functor(canvasElement, 'drop', this._onDrop);
if (!this.enablePointerEvents) {
functor(canvasElement, 'touchstart', this._onTouchStart, addEventOptions);
}
if (typeof eventjs !== 'undefined' && eventjsFunctor in eventjs) {
eventjs[eventjsFunctor](canvasElement, 'gesture', this._onGesture);
eventjs[eventjsFunctor](canvasElement, 'drag', this._onDrag);
Expand All @@ -81,9 +97,9 @@
removeListeners: function() {
this.addOrRemove(removeListener, 'remove');
// if you dispose on a mouseDown, before mouse up, you need to clean document to...
var eventTypePrefix = this.enablePointerEvents ? 'pointer' : 'mouse';
var eventTypePrefix = this._getEventPrefix();
removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
},
Expand All @@ -97,8 +113,10 @@
return;
}
this._onMouseDown = this._onMouseDown.bind(this);
this._onTouchStart = this._onTouchStart.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
this._onTouchEnd = this._onTouchEnd.bind(this);
this._onResize = this._onResize.bind(this);
this._onGesture = this._onGesture.bind(this);
this._onDrag = this._onDrag.bind(this);
Expand Down Expand Up @@ -238,29 +256,105 @@
this._resetTransformEventData(e);
},

/**
* Return a the id of an event.
* returns either the pointerId or the identifier or 0 for the mouse event
* @private
* @param {Event} evt Event object
*/
getPointerId: function(evt) {
var changedTouches = evt.changedTouches;

if (changedTouches) {
return changedTouches[0] && changedTouches[0].identifier;
}

if (this.enablePointerEvents) {
return evt.pointerId;
}

return -1;
},

/**
* Determines if an event has the id of the event that is considered main
* @private
* @param {evt} event Event object
*/
_isMainEvent: function(evt) {
if (evt.isPrimary === true) {
return true;
}
if (evt.isPrimary === false) {
return false;
}
if (evt.type === 'touchend' && evt.touches.length === 0) {
return true;
}
if (evt.changedTouches) {
return evt.changedTouches[0].identifier === this.mainTouchId;
}
return true;
},

/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDown: function (e) {
_onTouchStart: function(e) {
e.preventDefault();
if (this.mainTouchId === null) {
this.mainTouchId = this.getPointerId(e);
}
this.__onMouseDown(e);
this._resetTransformEventData();
addListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
addListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
// Unbind mousedown to prevent double triggers from touch devices
removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
},

/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDown: function (e) {
this.__onMouseDown(e);
this._resetTransformEventData();
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this.enablePointerEvents ? 'pointer' : 'mouse';
eventTypePrefix = this._getEventPrefix();
removeListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
removeListener(canvasElement, 'touchmove', this._onMouseMove, addEventOptions);
addListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
addListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
},

if (e.type === 'touchstart') {
// Unbind mousedown to prevent double triggers from touch devices
removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onTouchEnd: function(e) {
if (e.touches.length > 0) {
// if there are still touches stop here
return;
}
else {
addListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
addListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
this.__onMouseUp(e);
this._resetTransformEventData();
this.mainTouchId = null;
var eventTypePrefix = this._getEventPrefix();
removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
var _this = this;
if (this._willAddMouseDown) {
clearTimeout(this._willAddMouseDown);
}
this._willAddMouseDown = setTimeout(function() {
// Wait 400ms before rebinding mousedown to prevent double triggers
// from touch devices
addListener(_this.upperCanvasEl, eventTypePrefix + 'down', _this._onMouseDown);
_this._willAddMouseDown = 0;
}, 400);
},

/**
Expand All @@ -271,24 +365,11 @@
this.__onMouseUp(e);
this._resetTransformEventData();
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this.enablePointerEvents ? 'pointer' : 'mouse';

removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);

removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);

addListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
addListener(canvasElement, 'touchmove', this._onMouseMove, addEventOptions);

if (e.type === 'touchend') {
// Wait 400ms before rebinding mousedown to prevent double triggers
// from touch devices
var _this = this;
setTimeout(function() {
addListener(_this.upperCanvasEl, eventTypePrefix + 'down', _this._onMouseDown);
}, 400);
eventTypePrefix = this._getEventPrefix();
if (this._isMainEvent(e)) {
removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
addListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
}
},

Expand Down Expand Up @@ -368,6 +449,9 @@
return;
}

if (!this._isMainEvent(e)) {
return;
}
if (transform) {
this._finalizeCurrentTransform(e);
shouldRender = transform.actionPerformed;
Expand Down Expand Up @@ -556,12 +640,11 @@
* @param {Event} e Event object fired on mouseup
*/
_onMouseUpInDrawingMode: function(e) {
this._isCurrentlyDrawing = false;
if (this.clipTo) {
this.contextTop.restore();
}
var pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer });
this._isCurrentlyDrawing = this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer });
this._handleEvent(e, 'up');
},

Expand Down Expand Up @@ -597,6 +680,10 @@
return;
}

if (!this._isMainEvent(e)) {
return;
}

// ignore if some object is being transformed at this moment
if (this._currentTransform) {
return;
Expand Down Expand Up @@ -696,7 +783,8 @@
this._onMouseMoveInDrawingMode(e);
return;
}
if (typeof e.touches !== 'undefined' && e.touches.length > 1) {

if (!this._isMainEvent(e)) {
return;
}

Expand Down Expand Up @@ -928,7 +1016,6 @@
this.setCursor(this.defaultCursor);
return false;
}

var hoverCursor = target.hoverCursor || this.hoverCursor,
activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ?
this._activeObject : null,
Expand Down
Loading

0 comments on commit ef98a58

Please sign in to comment.