From 285562a7cbf67ca11b276fb369586264f61988f2 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 5 Oct 2021 14:27:33 +0300 Subject: [PATCH 1/4] fix an iOS15 issue where map stops when panning --- src/ui/map.js | 8 ++++---- test/unit/ui/map.test.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/ui/map.js b/src/ui/map.js index 80db60df2c3..47137da5239 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -696,9 +696,10 @@ class Map extends Camera { * if (mapDiv.style.visibility === true) map.resize(); */ resize(eventData?: Object) { - const dimensions = this._containerDimensions(); - const width = dimensions[0]; - const height = dimensions[1]; + const [width, height] = this._containerDimensions(); + + // do nothing if container remained the same size + if (width === this.transform.width && height === this.transform.height) return this; this._resizeCanvas(width, height); @@ -707,7 +708,6 @@ class Map extends Camera { const fireMoving = !this._moving; if (fireMoving) { - this.stop(); this.fire(new Event('movestart', eventData)) .fire(new Event('move', eventData)); } diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 484fc3a4f60..3767315f0b6 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -764,10 +764,43 @@ test('Map', (t) => { t.end(); }); + t.test('does nothing if container size is the same', (t) => { + const map = createMap(t), + container = map.getContainer(); + + t.spy(map.transform, 'resize'); + t.spy(map.painter, 'resize'); + + map.resize(); + + t.notOk(map.transform.resize.called); + t.notOk(map.painter.resize.called); + + t.end(); + }); + + t.test('does not call stop on resize', (t) => { + const map = createMap(t); + + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', + {value: () => ({height: 250, width: 250})}); + + t.spy(map, 'stop'); + + map.resize(); + + t.notOk(map.stop.called); + + t.end(); + }); + t.test('fires movestart, move, resize, and moveend events', (t) => { const map = createMap(t), events = []; + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', + {value: () => ({height: 250, width: 250})}); + ['movestart', 'move', 'resize', 'moveend'].forEach((event) => { map.on(event, (e) => { events.push(e.type); From a18856b594dbac8d1a03f25736ad6a6295c418b5 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 5 Oct 2021 15:35:02 +0300 Subject: [PATCH 2/4] fix tests and lint --- test/unit/ui/control/attribution.test.js | 4 ++++ test/unit/ui/control/logo.test.js | 4 ++++ test/unit/ui/map.test.js | 3 +-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/unit/ui/control/attribution.test.js b/test/unit/ui/control/attribution.test.js index 76891d6129c..4aa9d0364cf 100644 --- a/test/unit/ui/control/attribution.test.js +++ b/test/unit/ui/control/attribution.test.js @@ -37,6 +37,7 @@ test('AttributionControl appears in the position specified by the position optio test('AttributionControl appears in compact mode if compact option is used', (t) => { const map = createMap(t); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 700})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 700, configurable: true}); let attributionControl = new AttributionControl({ @@ -49,6 +50,7 @@ test('AttributionControl appears in compact mode if compact option is used', (t) t.equal(container.querySelectorAll('.mapboxgl-ctrl-attrib.mapboxgl-compact').length, 1); map.removeControl(attributionControl); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 600})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 600, configurable: true}); attributionControl = new AttributionControl({ compact: false @@ -61,6 +63,7 @@ test('AttributionControl appears in compact mode if compact option is used', (t) test('AttributionControl appears in compact mode if container is less then 640 pixel wide', (t) => { const map = createMap(t); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 700})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 700, configurable: true}); map.addControl(new AttributionControl()); @@ -68,6 +71,7 @@ test('AttributionControl appears in compact mode if container is less then 640 p t.equal(container.querySelectorAll('.mapboxgl-ctrl-attrib:not(.mapboxgl-compact)').length, 1); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 600})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 600, configurable: true}); map.resize(); diff --git a/test/unit/ui/control/logo.test.js b/test/unit/ui/control/logo.test.js index ea0c77f7825..a3491f55170 100644 --- a/test/unit/ui/control/logo.test.js +++ b/test/unit/ui/control/logo.test.js @@ -104,12 +104,16 @@ test('LogoControl appears in compact mode if container is less then 250 pixel wi const map = createMap(t); const container = map.getContainer(); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 255})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 255, configurable: true}); map.resize(); + t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo:not(.mapboxgl-compact)').length, 1); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 245})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 245, configurable: true}); map.resize(); + t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo.mapboxgl-compact').length, 1); t.end(); diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 3767315f0b6..deec79c18e9 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -765,8 +765,7 @@ test('Map', (t) => { }); t.test('does nothing if container size is the same', (t) => { - const map = createMap(t), - container = map.getContainer(); + const map = createMap(t); t.spy(map.transform, 'resize'); t.spy(map.painter, 'resize'); From d82b00f27ab7e8df293b0b05154c2e65fef50b7f Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Tue, 5 Oct 2021 10:34:16 -0700 Subject: [PATCH 3/4] Test drag pan handler does not end interaction on resize --- test/unit/ui/handler/drag_pan.test.js | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 1a476a32a47..f70d5c7702a 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -177,6 +177,37 @@ test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { t.end(); }); +test('DragPanHandler does not end a touch-triggered drag if the window resizes', (t) => { + const map = createMap(t); + const target = map.getCanvas(); + + const dragend = t.spy(); + map.on('dragend', dragend); + + const drag = t.spy(); + map.on('drag', drag); + + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); + map._renderTaskQueue.run(); + + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); + map._renderTaskQueue.run(); + + map.resize(); + + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 20, clientY: 10}]}); + map._renderTaskQueue.run(); + + simulate.touchend(map.getCanvas()); + map._renderTaskQueue.run(); + + t.equal(dragend.callCount, 1); + t.equal(drag.callCount, 2); + + map.remove(); + t.end(); +}); + test('DragPanHandler requests a new render frame after each mousemove event', (t) => { const map = createMap(t); const requestFrame = t.spy(map.handlers, '_requestFrame'); From 010c497daa074acb39ae3612cfffa7f65eb419eb Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Tue, 5 Oct 2021 14:27:49 -0700 Subject: [PATCH 4/4] Move blur event reset into non-touch handlers (#11087) * Move blur event reset into non-touch handlers * Fix linter * Fix/amend unit tests * Flush task queue in rotate test --- src/ui/handler/box_zoom.js | 4 ++++ src/ui/handler/click_zoom.js | 4 ++++ src/ui/handler/keyboard.js | 4 ++++ src/ui/handler/mouse.js | 4 ++++ src/ui/handler/scroll_zoom.js | 4 ++++ src/ui/handler_manager.js | 5 ----- test/unit/ui/handler/drag_pan.test.js | 8 ++++++-- test/unit/ui/handler/drag_rotate.test.js | 2 ++ 8 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/ui/handler/box_zoom.js b/src/ui/handler/box_zoom.js index 96c6dacac87..e76e3e117e1 100644 --- a/src/ui/handler/box_zoom.js +++ b/src/ui/handler/box_zoom.js @@ -153,6 +153,10 @@ class BoxZoomHandler { } } + blur() { + this.reset(); + } + reset() { this._active = false; diff --git a/src/ui/handler/click_zoom.js b/src/ui/handler/click_zoom.js index 19362ede8c0..44d29f26976 100644 --- a/src/ui/handler/click_zoom.js +++ b/src/ui/handler/click_zoom.js @@ -16,6 +16,10 @@ export default class ClickZoomHandler { this._active = false; } + blur() { + this.reset(); + } + dblclick(e: MouseEvent, point: Point) { e.preventDefault(); return { diff --git a/src/ui/handler/keyboard.js b/src/ui/handler/keyboard.js index eb04f773aae..b5bc103ff74 100644 --- a/src/ui/handler/keyboard.js +++ b/src/ui/handler/keyboard.js @@ -45,6 +45,10 @@ class KeyboardHandler { this._rotationDisabled = false; } + blur() { + this.reset(); + } + reset() { this._active = false; } diff --git a/src/ui/handler/mouse.js b/src/ui/handler/mouse.js index d1a94551476..059e024cea6 100644 --- a/src/ui/handler/mouse.js +++ b/src/ui/handler/mouse.js @@ -31,6 +31,10 @@ class MouseHandler { this._clickTolerance = options.clickTolerance || 1; } + blur() { + this.reset(); + } + reset() { this._active = false; this._moved = false; diff --git a/src/ui/handler/scroll_zoom.js b/src/ui/handler/scroll_zoom.js index 74084468297..0eb62460891 100644 --- a/src/ui/handler/scroll_zoom.js +++ b/src/ui/handler/scroll_zoom.js @@ -371,6 +371,10 @@ class ScrollZoomHandler { return easing; } + blur() { + this.reset(); + } + reset() { this._active = false; } diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index b9b4e3a12ca..a3fa767706c 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -344,11 +344,6 @@ class HandlerManager { handleEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { - if (e.type === 'blur') { - this.stop(true); - return; - } - this._updatingCamera = true; assert(e.timeStamp !== undefined); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index f70d5c7702a..98ea491a2ca 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -151,13 +151,15 @@ test('DragPanHandler ends a mouse-triggered drag if the window blurs', (t) => { map._renderTaskQueue.run(); simulate.blur(window); + map._renderTaskQueue.run(); + t.equal(dragend.callCount, 1); map.remove(); t.end(); }); -test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { +test('DragPanHandler does not end a touch-triggered drag if the window blurs', (t) => { const map = createMap(t); const target = map.getCanvas(); @@ -171,7 +173,9 @@ test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { map._renderTaskQueue.run(); simulate.blur(window); - t.equal(dragend.callCount, 1); + map._renderTaskQueue.run(); + + t.equal(dragend.callCount, 0); map.remove(); t.end(); diff --git a/test/unit/ui/handler/drag_rotate.test.js b/test/unit/ui/handler/drag_rotate.test.js index 4fe68a46fd2..f12c0eba8bb 100644 --- a/test/unit/ui/handler/drag_rotate.test.js +++ b/test/unit/ui/handler/drag_rotate.test.js @@ -492,6 +492,8 @@ test('DragRotateHandler ends rotation if the window blurs (#3389)', (t) => { t.equal(rotate.callCount, 1); simulate.blur(window); + map._renderTaskQueue.run(); + t.equal(rotateend.callCount, 1); map.remove();