From 6491e68a17c77062222d0b842cb839b405567b89 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sat, 29 Jun 2019 17:55:19 +0200 Subject: [PATCH 01/12] just save --- src/mixins/canvas_events.mixin.js | 112 +++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index c9500cccf37..4d75ca4da1f 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -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 === value - 1; } fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ { @@ -36,6 +36,22 @@ 'nw-resize' ], + /** + * Contains the list of pointers actually on the screen in mouseDown/mouseMove + * added on mousedown/pointerdown/touchstart, remove on mouseup/pointerup/touchend + * pointer format can change without notice until this array is marked as private. + * @type Number[] + * @private + */ + activePointers: null, + + /** + * Contains the id of the pointer that owns the fabric transform + * @type Number + * @private + */ + mainPointerId: null, + /** * Adds mouse listeners to canvas * @private @@ -44,14 +60,23 @@ // in case we initialized the class twice. This should not happen normally // but in some kind of applications where the canvas element may be changed // this is a workaround to having double listeners. + this.activePointers = []; this.removeListeners(); this._bindEvents(); 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); @@ -81,7 +106,7 @@ 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, eventTypePrefix + 'move', this._onMouseMove, addEventOptions); @@ -238,21 +263,80 @@ 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} event Event object + */ + getPointerId: function(event) { + if (this.enablePointerEvents) { + return event.pointerId; + } + + var changedTouches = event.changedTouches; + if (changedTouches) { + return changedTouches[0] && changedTouches[0].identifier; + } + + return 0; + }, + + /** + * Add a pointer to the active list. + * @private + * @param {Event} evt Event object + */ + _addPointer: function(evt) { + var id = this.getPointerId(evt), activePointers = this.activePointers; + activePointers.push(id); + if (activePointers.length === 1) { + // first event, the main one. + this.mainPointerId = id; + } + }, + + /** + * Determines if an event has the id of the event that is considered main + * @private + * @param {evt} event Event object + */ + _isMainEvent: function(evt) { + return this.getPointerId(evt) !== this.mainPointerId; + }, + + /** + * Remove a pointer from the active list. + * @private + * @param {evt} event Event object + */ + _removePointer: function(evt) { + var id = this.getPointerId(evt); + this.activePointers = this.activePointers.filter(function(_id) { + return _id !== id; + }); + if (id === this.mainPointerId) { + this.mainPointerId = null; + } + }, + /** * @private * @param {Event} e Event object fired on mousedown */ _onMouseDown: function (e) { + this._addPointer(e); this.__onMouseDown(e); this._resetTransformEventData(); addListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions); addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions); 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); + if (e.type === 'touchstart') { // Unbind mousedown to prevent double triggers from touch devices removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown); @@ -268,10 +352,11 @@ * @param {Event} e Event object fired on mouseup */ _onMouseUp: function (e) { + this._removePointer(e); this.__onMouseUp(e); this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, - eventTypePrefix = this.enablePointerEvents ? 'pointer' : 'mouse'; + eventTypePrefix = this._getEventPrefix(); removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp); removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions); @@ -368,6 +453,10 @@ return; } + if (!this._isMainEvent(e)) { + return; + } + if (transform) { this._finalizeCurrentTransform(e); shouldRender = transform.actionPerformed; @@ -534,7 +623,7 @@ fabric.util.clipContext(this, this.contextTop); } var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer }); + this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer, activePointers: this.activePointers }); this._handleEvent(e, 'down'); }, @@ -545,7 +634,7 @@ _onMouseMoveInDrawingMode: function(e) { if (this._isCurrentlyDrawing) { var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer }); + this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer, activePointers: this.activePointers }); } this.setCursor(this.freeDrawingCursor); this._handleEvent(e, 'move'); @@ -561,7 +650,7 @@ this.contextTop.restore(); } var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer }); + this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer, activePointers: this.activePointers }); this._handleEvent(e, 'up'); }, @@ -597,6 +686,10 @@ return; } + if (!this._isMainEvent(e)) { + return; + } + // ignore if some object is being transformed at this moment if (this._currentTransform) { return; @@ -696,7 +789,8 @@ this._onMouseMoveInDrawingMode(e); return; } - if (typeof e.touches !== 'undefined' && e.touches.length > 1) { + + if (!this._isMainEvent(e)) { return; } From 2c4cda1ebd3fa724c6c8a8eb7aaec7c676ef3ed8 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 07:13:28 -0700 Subject: [PATCH 02/12] make event detection dynamic --- src/canvas.class.js | 5 ---- src/mixins/canvas_events.mixin.js | 20 +++++++------- src/util/dom_event.js | 45 +++++++------------------------ 3 files changed, 20 insertions(+), 50 deletions(-) diff --git a/src/canvas.class.js b/src/canvas.class.js index bd26ac93766..a31e1eeada6 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -1720,9 +1720,4 @@ fabric.Canvas[prop] = fabric.StaticCanvas[prop]; } } - - if (fabric.isTouchSupported) { - /** @ignore */ - fabric.Canvas.prototype._setCursorFromEvent = function() { }; - } })(); diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 4d75ca4da1f..c1c4854fe01 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -16,7 +16,7 @@ addEventOptions = { passive: false }; function checkClick(e, value) { - return e.button === value - 1; + return e.button && (e.button === value - 1); } fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ { @@ -453,9 +453,9 @@ return; } - if (!this._isMainEvent(e)) { - return; - } + // if (!this._isMainEvent(e)) { + // // return; + // } if (transform) { this._finalizeCurrentTransform(e); @@ -686,9 +686,9 @@ return; } - if (!this._isMainEvent(e)) { - return; - } + // if (!this._isMainEvent(e)) { + // // return; + // } // ignore if some object is being transformed at this moment if (this._currentTransform) { @@ -790,9 +790,9 @@ return; } - if (!this._isMainEvent(e)) { - return; - } + // if (!this._isMainEvent(e)) { + // // return; + // } var groupSelector = this._groupSelector; diff --git a/src/util/dom_event.js b/src/util/dom_event.js index 81b32e3c980..bf7d1872ff2 100644 --- a/src/util/dom_event.js +++ b/src/util/dom_event.js @@ -191,46 +191,21 @@ function getPointer(event) { event || (event = fabric.window.event); - var element = event.target || - (typeof event.srcElement !== unknown ? event.srcElement : null), - - scroll = fabric.util.getScrollLeftTop(element); + var element = event.target, + scroll = fabric.util.getScrollLeftTop(element), + _evt = getTouchInfo(event); return { - x: pointerX(event) + scroll.left, - y: pointerY(event) + scroll.top + x: _evt.clientX + scroll.left, + y: _evt.clientY + scroll.top }; } - var pointerX = function(event) { - return event.clientX; - }, - - pointerY = function(event) { - return event.clientY; - }; - - function _getPointer(event, pageProp, clientProp) { - var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches'; - var pointer, eventTouchProp = event[touchProp]; - - if (eventTouchProp && eventTouchProp[0]) { - pointer = eventTouchProp[0][clientProp]; - } - - if (typeof pointer === 'undefined') { - pointer = event[clientProp]; + function getTouchInfo(event) { + var touchProp = event.changedTouches; + if (touchProp && touchProp[0]) { + return touchProp[0]; } - - return pointer; - } - - if (fabric.isTouchSupported) { - pointerX = function(event) { - return _getPointer(event, 'pageX', 'clientX'); - }; - pointerY = function(event) { - return _getPointer(event, 'pageY', 'clientY'); - }; + return event; } fabric.util.getPointer = getPointer; From 61b0ba7d1e4d68054bf398df35b5bbde5167ed86 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 08:02:24 -0700 Subject: [PATCH 03/12] seems better! --- src/mixins/canvas_events.mixin.js | 73 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index c1c4854fe01..21f94d05b0f 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -85,8 +85,7 @@ 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, 'touchstart', this._onTouchStart, addEventOptions); functor(canvasElement, 'dragover', this._onDragOver); functor(canvasElement, 'dragenter', this._onDragEnter); functor(canvasElement, 'dragleave', this._onDragLeave); @@ -108,7 +107,7 @@ // if you dispose on a mouseDown, before mouse up, you need to clean document to... 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); }, @@ -122,8 +121,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); @@ -324,27 +325,52 @@ * @private * @param {Event} e Event object fired on mousedown */ - _onMouseDown: function (e) { - this._addPointer(e); + _onTouchStart: function(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._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); + }, + /** + * @private + * @param {Event} e Event object fired on mousedown + */ + _onTouchEnd: function(e) { + this.__onMouseUp(e); + this._resetTransformEventData(); + var eventTypePrefix = this._getEventPrefix(); + removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions); + removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions); - if (e.type === 'touchstart') { - // Unbind mousedown to prevent double triggers from touch devices - removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown); - } - else { - addListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp); - addListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions); + // Wait 400ms before rebinding mousedown to prevent double triggers + // from touch devices + var _this = this; + if (this._willAddMouseDown) { + clearTimeout(this._willAddMouseDown); } + this._willAddMouseDown = setTimeout(function() { + addListener(_this.upperCanvasEl, eventTypePrefix + 'down', _this._onMouseDown); + _this._willAddMouseDown = 0; + }, 400); }, /** @@ -352,29 +378,13 @@ * @param {Event} e Event object fired on mouseup */ _onMouseUp: function (e) { - this._removePointer(e); this.__onMouseUp(e); this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); - 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); - } }, /** @@ -428,6 +438,7 @@ var target, transform = this._currentTransform, groupSelector = this._groupSelector, shouldRender = false, isClick = (!groupSelector || (groupSelector.left === 0 && groupSelector.top === 0)); + this._removePointer(e); this._cacheTransformEventData(e); target = this._target; this._handleEvent(e, 'up:before'); @@ -663,6 +674,7 @@ * @param {Event} e Event object fired on mousedown */ __onMouseDown: function (e) { + this._addPointer(e); this._cacheTransformEventData(e); this._handleEvent(e, 'down:before'); var target = this._target; @@ -1022,7 +1034,6 @@ this.setCursor(this.defaultCursor); return false; } - var hoverCursor = target.hoverCursor || this.hoverCursor, activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ? this._activeObject : null, From da3de925706e6738833a720804871bd151427e9b Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 08:27:40 -0700 Subject: [PATCH 04/12] seems better! --- src/util/dom_event.js | 174 ++---------------------------------------- 1 file changed, 8 insertions(+), 166 deletions(-) diff --git a/src/util/dom_event.js b/src/util/dom_event.js index bf7d1872ff2..be0873dcc0f 100644 --- a/src/util/dom_event.js +++ b/src/util/dom_event.js @@ -1,168 +1,6 @@ (function () { - - var unknown = 'unknown'; - - /* EVENT HANDLING */ - - function areHostMethods(object) { - var methodNames = Array.prototype.slice.call(arguments, 1), - t, i, len = methodNames.length; - for (i = 0; i < len; i++) { - t = typeof object[methodNames[i]]; - if (!(/^(?:function|object|unknown)$/).test(t)) { - return false; - } - } - return true; - } - - /** @ignore */ - var getElement, - setElement, - getUniqueId = (function () { - var uid = 0; - return function (element) { - return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++); - }; - })(); - - (function () { - var elements = { }; - /** @ignore */ - getElement = function (uid) { - return elements[uid]; - }; - /** @ignore */ - setElement = function (uid, element) { - elements[uid] = element; - }; - })(); - - function createListener(uid, handler) { - return { - handler: handler, - wrappedHandler: createWrappedHandler(uid, handler) - }; - } - - function createWrappedHandler(uid, handler) { - return function (e) { - handler.call(getElement(uid), e || fabric.window.event); - }; - } - - function createDispatcher(uid, eventName) { - return function (e) { - if (handlers[uid] && handlers[uid][eventName]) { - var handlersForEvent = handlers[uid][eventName]; - for (var i = 0, len = handlersForEvent.length; i < len; i++) { - handlersForEvent[i].call(this, e || fabric.window.event); - } - } - }; - } - - var shouldUseAddListenerRemoveListener = ( - areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') && - areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')), - - shouldUseAttachEventDetachEvent = ( - areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') && - areHostMethods(fabric.window, 'attachEvent', 'detachEvent')), - - // IE branch - listeners = { }, - - // DOM L0 branch - handlers = { }, - - addListener, removeListener; - - if (shouldUseAddListenerRemoveListener) { - /** @ignore */ - addListener = function (element, eventName, handler, options) { - // since ie10 or ie9 can use addEventListener but they do not support options, i need to check - element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); - }; - /** @ignore */ - removeListener = function (element, eventName, handler, options) { - element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); - }; - } - - else if (shouldUseAttachEventDetachEvent) { - /** @ignore */ - addListener = function (element, eventName, handler) { - if (!element) { - return; - } - var uid = getUniqueId(element); - setElement(uid, element); - if (!listeners[uid]) { - listeners[uid] = { }; - } - if (!listeners[uid][eventName]) { - listeners[uid][eventName] = []; - - } - var listener = createListener(uid, handler); - listeners[uid][eventName].push(listener); - element.attachEvent('on' + eventName, listener.wrappedHandler); - }; - /** @ignore */ - removeListener = function (element, eventName, handler) { - if (!element) { - return; - } - var uid = getUniqueId(element), listener; - if (listeners[uid] && listeners[uid][eventName]) { - for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) { - listener = listeners[uid][eventName][i]; - if (listener && listener.handler === handler) { - element.detachEvent('on' + eventName, listener.wrappedHandler); - listeners[uid][eventName][i] = null; - } - } - } - }; - } - else { - /** @ignore */ - addListener = function (element, eventName, handler) { - if (!element) { - return; - } - var uid = getUniqueId(element); - if (!handlers[uid]) { - handlers[uid] = { }; - } - if (!handlers[uid][eventName]) { - handlers[uid][eventName] = []; - var existingHandler = element['on' + eventName]; - if (existingHandler) { - handlers[uid][eventName].push(existingHandler); - } - element['on' + eventName] = createDispatcher(uid, eventName); - } - handlers[uid][eventName].push(handler); - }; - /** @ignore */ - removeListener = function (element, eventName, handler) { - if (!element) { - return; - } - var uid = getUniqueId(element); - if (handlers[uid] && handlers[uid][eventName]) { - var handlersForEvent = handlers[uid][eventName]; - for (var i = 0, len = handlersForEvent.length; i < len; i++) { - if (handlersForEvent[i] === handler) { - handlersForEvent.splice(i, 1); - } - } - } - }; - } - + // since ie10 or ie9 can use addEventListener but they do not support options, i need to check + var couldUseAttachEvent = !!fabric.document.createElement('div').attachEvent; /** * Adds an event listener to an element * @function @@ -171,7 +9,9 @@ * @param {String} eventName * @param {Function} handler */ - fabric.util.addListener = addListener; + fabric.util.addListener = function (element, eventName, handler, options) { + element && element.addEventListener(eventName, handler, couldUseAttachEvent ? false : options); + }; /** * Removes an event listener from an element @@ -181,7 +21,9 @@ * @param {String} eventName * @param {Function} handler */ - fabric.util.removeListener = removeListener; + fabric.util.removeListener = function (element, eventName, handler, options) { + element && element.removeEventListener(eventName, handler, couldUseAttachEvent ? false : options); + }; /** * Cross-browser wrapper for getting event's coordinates From b6a53c579087de6fc52b4a9081118ffa29c536e9 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 08:29:08 -0700 Subject: [PATCH 05/12] seems better! --- src/util/dom_event.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util/dom_event.js b/src/util/dom_event.js index be0873dcc0f..3d3243cdc79 100644 --- a/src/util/dom_event.js +++ b/src/util/dom_event.js @@ -31,8 +31,6 @@ * @param {Event} event Event object */ function getPointer(event) { - event || (event = fabric.window.event); - var element = event.target, scroll = fabric.util.getScrollLeftTop(element), _evt = getTouchInfo(event); From 4e07704f3cb767e47be4a1444aec7898e4a01008 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 09:18:16 -0700 Subject: [PATCH 06/12] fixed test --- package.json | 2 +- src/util/dom_event.js | 30 +++++++++++------------------- test/unit/canvas_events.js | 10 +++++----- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 9b935c9f2f6..e619ca55e15 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ }, "optionalDependencies": { "canvas": "^2.6.0", - "jsdom": "15.1.0" + "jsdom": "^15.1.0" }, "devDependencies": { "eslint": "4.18.x", diff --git a/src/util/dom_event.js b/src/util/dom_event.js index 3d3243cdc79..221717481d5 100644 --- a/src/util/dom_event.js +++ b/src/util/dom_event.js @@ -9,7 +9,7 @@ * @param {String} eventName * @param {Function} handler */ - fabric.util.addListener = function (element, eventName, handler, options) { + fabric.util.addListener = function(element, eventName, handler, options) { element && element.addEventListener(eventName, handler, couldUseAttachEvent ? false : options); }; @@ -21,25 +21,10 @@ * @param {String} eventName * @param {Function} handler */ - fabric.util.removeListener = function (element, eventName, handler, options) { + fabric.util.removeListener = function(element, eventName, handler, options) { element && element.removeEventListener(eventName, handler, couldUseAttachEvent ? false : options); }; - /** - * Cross-browser wrapper for getting event's coordinates - * @memberOf fabric.util - * @param {Event} event Event object - */ - function getPointer(event) { - var element = event.target, - scroll = fabric.util.getScrollLeftTop(element), - _evt = getTouchInfo(event); - return { - x: _evt.clientX + scroll.left, - y: _evt.clientY + scroll.top - }; - } - function getTouchInfo(event) { var touchProp = event.changedTouches; if (touchProp && touchProp[0]) { @@ -48,6 +33,13 @@ return event; } - fabric.util.getPointer = getPointer; - + fabric.util.getPointer = function(event) { + var element = event.target, + scroll = fabric.util.getScrollLeftTop(element), + _evt = getTouchInfo(event); + return { + x: _evt.clientX + scroll.left, + y: _evt.clientY + scroll.top + }; + }; })(); diff --git a/test/unit/canvas_events.js b/test/unit/canvas_events.js index cd1892458bb..369ed91ab2e 100644 --- a/test/unit/canvas_events.js +++ b/test/unit/canvas_events.js @@ -103,19 +103,19 @@ canvas.fireRightClick = false; canvas._currentTransform = false; canvas.isDrawingMode = false; - canvas.__onMouseDown({ which: 1, target: canvas.upperCanvasEl }); + canvas.__onMouseDown({ button: 0, target: canvas.upperCanvasEl }); assert.equal(clickCount, 1, 'mouse down fired'); clickCount = 0; - canvas.__onMouseDown({ which: 3, target: canvas.upperCanvasEl }); + canvas.__onMouseDown({ button: 2, target: canvas.upperCanvasEl }); assert.equal(clickCount, 0, 'rightclick did not fire a mouse:down event'); canvas.fireRightClick = true; - canvas.__onMouseDown({ which: 3, target: canvas.upperCanvasEl }); + canvas.__onMouseDown({ button: 2, target: canvas.upperCanvasEl }); assert.equal(clickCount, 1, 'rightclick did fire a mouse:down event'); clickCount = 0; - canvas.__onMouseDown({ which: 2, target: canvas.upperCanvasEl }); + canvas.__onMouseDown({ button: 1, target: canvas.upperCanvasEl }); assert.equal(clickCount, 0, 'middleClick did not fire a mouse:down event'); canvas.fireMiddleClick = true; - canvas.__onMouseDown({ which: 2, target: canvas.upperCanvasEl }); + canvas.__onMouseDown({ button: 1, target: canvas.upperCanvasEl }); assert.equal(clickCount, 1, 'middleClick did fire a mouse:down event'); }); From 077a20773ba3f105bbcdcadc130c735daf1c4ac4 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sun, 30 Jun 2019 15:25:07 -0700 Subject: [PATCH 07/12] working queue with add and remove --- src/mixins/canvas_events.mixin.js | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 21f94d05b0f..163c1243046 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -303,7 +303,7 @@ * @param {evt} event Event object */ _isMainEvent: function(evt) { - return this.getPointerId(evt) !== this.mainPointerId; + return this.activePointers.length <= 1 || this.getPointerId(evt) === this.mainPointerId; }, /** @@ -330,6 +330,9 @@ this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); + if (this.activePointers.length > 1) { + return + } addListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions); addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions); // Unbind mousedown to prevent double triggers from touch devices @@ -356,18 +359,21 @@ */ _onTouchEnd: function(e) { this.__onMouseUp(e); + this._removePointer(e); this._resetTransformEventData(); + if (this.activePointers.length > 0) { + return; + } var eventTypePrefix = this._getEventPrefix(); removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions); removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions); - - // Wait 400ms before rebinding mousedown to prevent double triggers - // from touch devices 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); @@ -379,6 +385,7 @@ */ _onMouseUp: function (e) { this.__onMouseUp(e); + this._removePointer(e); this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); @@ -438,7 +445,6 @@ var target, transform = this._currentTransform, groupSelector = this._groupSelector, shouldRender = false, isClick = (!groupSelector || (groupSelector.left === 0 && groupSelector.top === 0)); - this._removePointer(e); this._cacheTransformEventData(e); target = this._target; this._handleEvent(e, 'up:before'); @@ -464,10 +470,9 @@ return; } - // if (!this._isMainEvent(e)) { - // // return; - // } - + if (!this._isMainEvent(e)) { + return; + } if (transform) { this._finalizeCurrentTransform(e); shouldRender = transform.actionPerformed; @@ -698,9 +703,9 @@ return; } - // if (!this._isMainEvent(e)) { - // // return; - // } + if (!this._isMainEvent(e)) { + return; + } // ignore if some object is being transformed at this moment if (this._currentTransform) { @@ -802,9 +807,9 @@ return; } - // if (!this._isMainEvent(e)) { - // // return; - // } + if (!this._isMainEvent(e)) { + return; + } var groupSelector = this._groupSelector; From 084ff91394cc28a5c8ce47c4282aacc7c4c9a5e7 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sat, 13 Jul 2019 09:07:35 -0700 Subject: [PATCH 08/12] ok better --- src/canvas.class.js | 3 +- src/mixins/canvas_events.mixin.js | 96 ++++++++++++------------------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/src/canvas.class.js b/src/canvas.class.js index a31e1eeada6..0cf54d6e18b 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -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; diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 163c1243046..52ef175dcf8 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -37,20 +37,11 @@ ], /** - * Contains the list of pointers actually on the screen in mouseDown/mouseMove - * added on mousedown/pointerdown/touchstart, remove on mouseup/pointerup/touchend - * pointer format can change without notice until this array is marked as private. - * @type Number[] - * @private - */ - activePointers: null, - - /** - * Contains the id of the pointer that owns the fabric transform + * Contains the id of the touch event that owns the fabric transform * @type Number * @private */ - mainPointerId: null, + mainTouchId: null, /** * Adds mouse listeners to canvas @@ -60,7 +51,6 @@ // in case we initialized the class twice. This should not happen normally // but in some kind of applications where the canvas element may be changed // this is a workaround to having double listeners. - this.activePointers = []; this.removeListeners(); this._bindEvents(); this.addOrRemove(addListener, 'add'); @@ -85,11 +75,13 @@ functor(canvasElement, 'wheel', this._onMouseWheel); functor(canvasElement, 'contextmenu', this._onContextMenu); functor(canvasElement, 'dblclick', this._onDoubleClick); - functor(canvasElement, 'touchstart', this._onTouchStart, 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); @@ -268,33 +260,20 @@ * Return a the id of an event. * returns either the pointerId or the identifier or 0 for the mouse event * @private - * @param {Event} event Event object + * @param {Event} evt Event object */ - getPointerId: function(event) { - if (this.enablePointerEvents) { - return event.pointerId; - } + getPointerId: function(evt) { + var changedTouches = evt.changedTouches; - var changedTouches = event.changedTouches; if (changedTouches) { return changedTouches[0] && changedTouches[0].identifier; } - return 0; - }, - - /** - * Add a pointer to the active list. - * @private - * @param {Event} evt Event object - */ - _addPointer: function(evt) { - var id = this.getPointerId(evt), activePointers = this.activePointers; - activePointers.push(id); - if (activePointers.length === 1) { - // first event, the main one. - this.mainPointerId = id; + if (this.enablePointerEvents) { + return evt.pointerId; } + + return -1; }, /** @@ -303,22 +282,19 @@ * @param {evt} event Event object */ _isMainEvent: function(evt) { - return this.activePointers.length <= 1 || this.getPointerId(evt) === this.mainPointerId; - }, - - /** - * Remove a pointer from the active list. - * @private - * @param {evt} event Event object - */ - _removePointer: function(evt) { - var id = this.getPointerId(evt); - this.activePointers = this.activePointers.filter(function(_id) { - return _id !== id; - }); - if (id === this.mainPointerId) { - this.mainPointerId = null; + 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; }, /** @@ -326,13 +302,14 @@ * @param {Event} e Event object fired on mousedown */ _onTouchStart: function(e) { + e.preventDefault(); + if (this.mainTouchId === null) { + this.mainTouchId = this.getPointerId(e); + } this.__onMouseDown(e); this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); - if (this.activePointers.length > 1) { - return - } addListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions); addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions); // Unbind mousedown to prevent double triggers from touch devices @@ -358,12 +335,13 @@ * @param {Event} e Event object fired on mousedown */ _onTouchEnd: function(e) { - this.__onMouseUp(e); - this._removePointer(e); - this._resetTransformEventData(); - if (this.activePointers.length > 0) { + if (e.touches.length > 0) { + // if there are still touches stop here return; } + 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); @@ -385,7 +363,6 @@ */ _onMouseUp: function (e) { this.__onMouseUp(e); - this._removePointer(e); this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); @@ -639,7 +616,7 @@ fabric.util.clipContext(this, this.contextTop); } var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer, activePointers: this.activePointers }); + this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer }); this._handleEvent(e, 'down'); }, @@ -650,7 +627,7 @@ _onMouseMoveInDrawingMode: function(e) { if (this._isCurrentlyDrawing) { var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer, activePointers: this.activePointers }); + this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer }); } this.setCursor(this.freeDrawingCursor); this._handleEvent(e, 'move'); @@ -666,7 +643,7 @@ this.contextTop.restore(); } var pointer = this.getPointer(e); - this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer, activePointers: this.activePointers }); + this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer }); this._handleEvent(e, 'up'); }, @@ -679,7 +656,6 @@ * @param {Event} e Event object fired on mousedown */ __onMouseDown: function (e) { - this._addPointer(e); this._cacheTransformEventData(e); this._handleEvent(e, 'down:before'); var target = this._target; From 7864a8b2c2e05e76a8de2c2c80be54c97ac41f14 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sat, 13 Jul 2019 12:31:01 -0700 Subject: [PATCH 09/12] pencil working --- src/brushes/pencil_brush.class.js | 20 +++++++++++++++++--- src/mixins/canvas_events.mixin.js | 11 ++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 69bb5a3b451..848ccc5969e 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -37,7 +37,12 @@ * Inovoked on mouse down * @param {Object} pointer */ - onMouseDown: function(pointer) { + onMouseDown: function(pointer, options) { + if (!this.canvas._isMainEvent(options.e)) { + console.log('NOT MAIN DOWN') + return; + } + console.log('MAIN DOWN') this._prepareForDrawing(pointer); // capture coordinates immediately // this allows to draw dots (when movement never occurs) @@ -49,7 +54,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 @@ -75,9 +83,15 @@ /** * Invoked on mouse up */ - onMouseUp: function() { + onMouseUp: function(options) { + if (!this.canvas._isMainEvent(options.e)) { + console.log('NOT MAIN') + return true; + } + console.log('MAIN') this.oldEnd = undefined; this._finalizeAndAddPath(); + return false; }, /** diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 52ef175dcf8..c88ad098deb 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -366,9 +366,11 @@ this._resetTransformEventData(); var canvasElement = this.upperCanvasEl, eventTypePrefix = this._getEventPrefix(); - removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp); - removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions); - addListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions); + 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); + } }, /** @@ -638,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'); }, From 6ea500edd8201f02d0109cdbf2bb8168764fc2d7 Mon Sep 17 00:00:00 2001 From: Asturur Date: Sat, 13 Jul 2019 12:34:06 -0700 Subject: [PATCH 10/12] no console.log --- src/brushes/pencil_brush.class.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 848ccc5969e..7c6568a2b97 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -39,10 +39,8 @@ */ onMouseDown: function(pointer, options) { if (!this.canvas._isMainEvent(options.e)) { - console.log('NOT MAIN DOWN') return; } - console.log('MAIN DOWN') this._prepareForDrawing(pointer); // capture coordinates immediately // this allows to draw dots (when movement never occurs) @@ -85,10 +83,8 @@ */ onMouseUp: function(options) { if (!this.canvas._isMainEvent(options.e)) { - console.log('NOT MAIN') return true; } - console.log('MAIN') this.oldEnd = undefined; this._finalizeAndAddPath(); return false; From d0d4ad3bb5b49becabd8e7f66bbb8d113ed4a37e Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 14 Jul 2019 16:51:41 +0200 Subject: [PATCH 11/12] fix tests --- test/unit/brushes.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/unit/brushes.js b/test/unit/brushes.js index 2d1f1080e05..70ce2bf750d 100644 --- a/test/unit/brushes.js +++ b/test/unit/brushes.js @@ -24,18 +24,18 @@ QUnit.test('fabric pencil brush draw point', function(assert) { var brush = new fabric.PencilBrush(canvas); var pointer = canvas.getPointer({ clientX: 10, clientY: 10}); - brush.onMouseDown(pointer); + brush.onMouseDown(pointer, { e: {} }); var pathData = brush.convertPointsToSVGPath(brush._points).join(''); assert.equal(pathData, 'M 9.999 10 L 10.001 10', 'path data create a small line that looks like a point'); }); QUnit.test('fabric pencil brush multiple points', function(assert) { var brush = new fabric.PencilBrush(canvas); var pointer = canvas.getPointer({ clientX: 10, clientY: 10}); - brush.onMouseDown(pointer); - brush.onMouseMove(pointer); - brush.onMouseMove(pointer); - brush.onMouseMove(pointer); - brush.onMouseMove(pointer); + brush.onMouseDown(pointer, { e: {} }); + brush.onMouseMove(pointer, { e: {} }); + brush.onMouseMove(pointer, { e: {} }); + brush.onMouseMove(pointer, { e: {} }); + brush.onMouseMove(pointer, { e: {} }); var pathData = brush.convertPointsToSVGPath(brush._points).join(''); assert.equal(pathData, 'M 9.999 10 L 10.001 10', 'path data create a small line that looks like a point'); assert.equal(brush._points.length, 2, 'concident points are discarded'); @@ -45,11 +45,11 @@ var pointer = canvas.getPointer({ clientX: 10, clientY: 10}); var pointer2 = canvas.getPointer({ clientX: 15, clientY: 15}); var pointer3 = canvas.getPointer({ clientX: 20, clientY: 20}); - brush.onMouseDown(pointer); - brush.onMouseMove(pointer2); - brush.onMouseMove(pointer3); - brush.onMouseMove(pointer2); - brush.onMouseMove(pointer3); + brush.onMouseDown(pointer, { e: {} }); + brush.onMouseMove(pointer2, { e: {} }); + brush.onMouseMove(pointer3, { e: {} }); + brush.onMouseMove(pointer2, { e: {} }); + brush.onMouseMove(pointer3, { e: {} }); var pathData = brush.convertPointsToSVGPath(brush._points).join(''); assert.equal(pathData, 'M 9.999 9.999 Q 10 10 12.5 12.5 Q 15 15 17.5 17.5 Q 20 20 17.5 17.5 Q 15 15 17.5 17.5 L 20.001 20.001', 'path data create a complex path'); assert.equal(brush._points.length, 6, 'concident points are discarded'); @@ -65,12 +65,12 @@ var pointer = canvas.getPointer({ clientX: 10, clientY: 10}); var pointer2 = canvas.getPointer({ clientX: 15, clientY: 15}); var pointer3 = canvas.getPointer({ clientX: 20, clientY: 20}); - brush.onMouseDown(pointer); - brush.onMouseMove(pointer2); - brush.onMouseMove(pointer3); - brush.onMouseMove(pointer2); - brush.onMouseMove(pointer3); - brush.onMouseUp(pointer3); + brush.onMouseDown(pointer, { e: {} }); + brush.onMouseMove(pointer2, { e: {} }); + brush.onMouseMove(pointer3, { e: {} }); + brush.onMouseMove(pointer2, { e: {} }); + brush.onMouseMove(pointer3, { e: {} }); + brush.onMouseUp({ e: {} }); assert.equal(fired, true, 'event is fired'); assert.ok(added instanceof fabric.Path, 'a path is added'); assert.ok(added.path.length, 6, 'path has 6 steps'); From 25146244f4cf4a7fe8a22d7a13ce4e352f384001 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 14 Jul 2019 18:03:36 +0200 Subject: [PATCH 12/12] more test fixes --- test/visual/freedraw.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/visual/freedraw.js b/test/visual/freedraw.js index 3e5c7c44f06..97a93149c9f 100644 --- a/test/visual/freedraw.js +++ b/test/visual/freedraw.js @@ -34,15 +34,15 @@ else { visualTestLoop = window.visualTestLoop; } - + var options = { e: { pointerId: 1 } }; function pointDrawer(points, brush) { - brush.onMouseDown(points[0]); + brush.onMouseDown(points[0], options); for (var i = 1; i < points.length; i++) { points[i].x = parseFloat(points[i].x); points[i].y = parseFloat(points[i].y); - brush.onMouseMove(points[i]); + brush.onMouseMove(points[i], options); } - brush.onMouseUp(); + brush.onMouseUp(options); } var tests = [];