diff --git a/src/events/pointer.js b/src/events/pointer.js index 3fc8560302..457009c2bf 100644 --- a/src/events/pointer.js +++ b/src/events/pointer.js @@ -9,6 +9,7 @@ function pointer(p5, fn, lifecycles){ const events = [ 'pointerdown', 'pointerup', + 'pointercancel', 'pointermove', 'dragend', 'dragover', @@ -1394,6 +1395,16 @@ function pointer(p5, fn, lifecycles){ fn._ondragend = fn._onpointerup; fn._ondragover = fn._onpointermove; + fn._onpointercancel = function(e) { + this._activePointers.delete(e.pointerId); + this._setMouseButton(e); + this._updatePointerCoords(e); + + if (this._activePointers.size === 0) { + this.mouseIsPressed = false; + } + }; + /** * A function that's called once after a mouse button is pressed and released. * diff --git a/test/unit/events/touch.js b/test/unit/events/touch.js index f17eb7387a..f1091e5b6e 100644 --- a/test/unit/events/touch.js +++ b/test/unit/events/touch.js @@ -32,6 +32,13 @@ suite('Touch Events', function() { myp5.remove(); }); + beforeEach(function() { + // Reset pointer state so tests don't leak active pointers into each other. + myp5._activePointers.clear(); + myp5.touches = []; + myp5.mouseIsPressed = false; + }); + suite('p5.prototype.touches', function() { test('should be an empty array', function() { assert.deepEqual(myp5.touches, []); @@ -44,7 +51,45 @@ suite('Touch Events', function() { }); test('should contain the touch registered', function() { + window.dispatchEvent(touchEvent1); assert.strictEqual(myp5.touches[0].id, 1); }); }); + + suite('p5.prototype._onpointercancel', function() { + test('should remove the cancelled touch from touches', function() { + window.dispatchEvent(touchEvent1); + window.dispatchEvent(touchEvent2); + assert.strictEqual(myp5.touches.length, 2); + + // A cancelled pointer must be cleaned up even though no + // 'pointerup' event is dispatched. + const cancelEvent = new PointerEvent('pointercancel', { + pointerId: 1, + clientX: 100, + clientY: 100, + pointerType: 'touch' + }); + window.dispatchEvent(cancelEvent); + + assert.strictEqual(myp5.touches.length, 1); + assert.strictEqual(myp5.touches[0].id, 2); + }); + + test('should reset mouseIsPressed once all pointers are cancelled', function() { + window.dispatchEvent(touchEvent1); + assert.strictEqual(myp5.mouseIsPressed, true); + + const cancelEvent = new PointerEvent('pointercancel', { + pointerId: 1, + clientX: 100, + clientY: 100, + pointerType: 'touch' + }); + window.dispatchEvent(cancelEvent); + + assert.strictEqual(myp5.touches.length, 0); + assert.strictEqual(myp5.mouseIsPressed, false); + }); + }); });