diff --git a/src/ui/handler/handler_util.js b/src/ui/handler/handler_util.js index e26f8a80d5b..493f93d6264 100644 --- a/src/ui/handler/handler_util.js +++ b/src/ui/handler/handler_util.js @@ -2,7 +2,7 @@ import assert from 'assert'; -export function indexTouches(touches: TouchList, points: Array) { +export function indexTouches(touches: Array, points: Array) { assert(touches.length === points.length); const obj = {}; for (let i = 0; i < touches.length; i++) { diff --git a/src/ui/handler/tap_drag_zoom.js b/src/ui/handler/tap_drag_zoom.js index c1e57bf931f..d8bf435a2c5 100644 --- a/src/ui/handler/tap_drag_zoom.js +++ b/src/ui/handler/tap_drag_zoom.js @@ -30,7 +30,7 @@ export default class TapDragZoomHandler { this._tap.reset(); } - touchstart(e: TouchEvent, points: Array) { + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { if (this._swipePoint) return; if (this._tapTime && e.timeStamp - this._tapTime > MAX_TAP_INTERVAL) { @@ -38,19 +38,19 @@ export default class TapDragZoomHandler { } if (!this._tapTime) { - this._tap.touchstart(e, points); - } else if (e.targetTouches.length > 0) { + this._tap.touchstart(e, points, mapTouches); + } else if (mapTouches.length > 0) { this._swipePoint = points[0]; - this._swipeTouch = e.targetTouches[0].identifier; + this._swipeTouch = mapTouches[0].identifier; } } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._tapTime) { - this._tap.touchmove(e, points); + this._tap.touchmove(e, points, mapTouches); } else if (this._swipePoint) { - if (e.targetTouches[0].identifier !== this._swipeTouch) { + if (mapTouches[0].identifier !== this._swipeTouch) { return; } @@ -67,14 +67,14 @@ export default class TapDragZoomHandler { } } - touchend(e: TouchEvent) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._tapTime) { - const point = this._tap.touchend(e); + const point = this._tap.touchend(e, points, mapTouches); if (point) { this._tapTime = e.timeStamp; } } else if (this._swipePoint) { - if (e.targetTouches.length === 0) { + if (mapTouches.length === 0) { this.reset(); } } diff --git a/src/ui/handler/tap_recognizer.js b/src/ui/handler/tap_recognizer.js index 40328e19764..01e9dee629e 100644 --- a/src/ui/handler/tap_recognizer.js +++ b/src/ui/handler/tap_recognizer.js @@ -35,9 +35,9 @@ export class SingleTapRecognizer { this.aborted = false; } - touchstart(e: TouchEvent, points: Array) { + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { - if (this.centroid || e.targetTouches.length > this.numTouches) { + if (this.centroid || mapTouches.length > this.numTouches) { this.aborted = true; } if (this.aborted) { @@ -48,16 +48,16 @@ export class SingleTapRecognizer { this.startTime = e.timeStamp; } - if (e.targetTouches.length === this.numTouches) { + if (mapTouches.length === this.numTouches) { this.centroid = getCentroid(points); - this.touches = indexTouches(e.targetTouches, points); + this.touches = indexTouches(mapTouches, points); } } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (this.aborted || !this.centroid) return; - const newTouches = indexTouches(e.targetTouches, points); + const newTouches = indexTouches(mapTouches, points); for (const id in this.touches) { const prevPos = this.touches[id]; const pos = newTouches[id]; @@ -67,12 +67,12 @@ export class SingleTapRecognizer { } } - touchend(e: TouchEvent) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) { this.aborted = true; } - if (e.targetTouches.length === 0) { + if (mapTouches.length === 0) { const centroid = !this.aborted && this.centroid; this.reset(); if (centroid) return centroid; @@ -102,16 +102,16 @@ export class TapRecognizer { this.singleTap.reset(); } - touchstart(e: TouchEvent, points: Array) { - this.singleTap.touchstart(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + this.singleTap.touchstart(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { - this.singleTap.touchmove(e, points); + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { + this.singleTap.touchmove(e, points, mapTouches); } - touchend(e: TouchEvent) { - const tap = this.singleTap.touchend(e); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + const tap = this.singleTap.touchend(e, points, mapTouches); if (tap) { const soonEnough = e.timeStamp - this.lastTime < MAX_TAP_INTERVAL; const closeEnough = !this.lastTap || this.lastTap.dist(tap) < MAX_DIST; diff --git a/src/ui/handler/tap_zoom.js b/src/ui/handler/tap_zoom.js index 0249c1069e9..1aee59d1e88 100644 --- a/src/ui/handler/tap_zoom.js +++ b/src/ui/handler/tap_zoom.js @@ -31,19 +31,19 @@ export default class TapZoomHandler { this._zoomOut.reset(); } - touchstart(e: TouchEvent, points: Array) { - this._zoomIn.touchstart(e, points); - this._zoomOut.touchstart(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + this._zoomIn.touchstart(e, points, mapTouches); + this._zoomOut.touchstart(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { - this._zoomIn.touchmove(e, points); - this._zoomOut.touchmove(e, points); + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { + this._zoomIn.touchmove(e, points, mapTouches); + this._zoomOut.touchmove(e, points, mapTouches); } - touchend(e: TouchEvent) { - const zoomInPoint = this._zoomIn.touchend(e); - const zoomOutPoint = this._zoomOut.touchend(e); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches); + const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches); if (zoomInPoint) { this._active = true; diff --git a/src/ui/handler/touch_pan.js b/src/ui/handler/touch_pan.js index a04a7f7f1d6..be63664b6bd 100644 --- a/src/ui/handler/touch_pan.js +++ b/src/ui/handler/touch_pan.js @@ -24,20 +24,20 @@ export default class TouchPanHandler { this._sum = new Point(0, 0); } - touchstart(e: TouchEvent, points: Array) { - return this._calculateTransform(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + return this._calculateTransform(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._active) return; e.preventDefault(); - return this._calculateTransform(e, points); + return this._calculateTransform(e, points, mapTouches); } - touchend(e: TouchEvent, points: Array) { - this._calculateTransform(e, points); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + this._calculateTransform(e, points, mapTouches); - if (this._active && e.targetTouches.length < this._minTouches) { + if (this._active && mapTouches.length < this._minTouches) { this.reset(); } } @@ -46,10 +46,10 @@ export default class TouchPanHandler { this.reset(); } - _calculateTransform(e: TouchEvent, points: Array) { - if (e.targetTouches.length > 0) this._active = true; + _calculateTransform(e: TouchEvent, points: Array, mapTouches: Array) { + if (mapTouches.length > 0) this._active = true; - const touches = indexTouches(e.targetTouches, points); + const touches = indexTouches(mapTouches, points); const touchPointSum = new Point(0, 0); const touchDeltaSum = new Point(0, 0); diff --git a/src/ui/handler/touch_zoom_rotate.js b/src/ui/handler/touch_zoom_rotate.js index d7b7743b5b9..8e7b910c205 100644 --- a/src/ui/handler/touch_zoom_rotate.js +++ b/src/ui/handler/touch_zoom_rotate.js @@ -24,26 +24,28 @@ class TwoTouchHandler { _start(points: [Point, Point]) {} //eslint-disable-line _move(points: [Point, Point], pinchAround: Point, e: TouchEvent) { return {}; } //eslint-disable-line - touchstart(e: TouchEvent, points: Array) { - if (this._firstTwoTouches || e.targetTouches.length < 2) return; + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + //console.log(e.target, e.targetTouches.length ? e.targetTouches[0].target : null); + //log('touchstart', points, e.target.innerHTML, e.targetTouches.length ? e.targetTouches[0].target.innerHTML: undefined); + if (this._firstTwoTouches || mapTouches.length < 2) return; this._firstTwoTouches = [ - e.targetTouches[0].identifier, - e.targetTouches[1].identifier + mapTouches[0].identifier, + mapTouches[1].identifier ]; // implemented by child classes this._start([points[0], points[1]]); } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._firstTwoTouches) return; e.preventDefault(); const [idA, idB] = this._firstTwoTouches; - const a = getTouchById(e, points, idA); - const b = getTouchById(e, points, idB); + const a = getTouchById(mapTouches, points, idA); + const b = getTouchById(mapTouches, points, idB); if (!a || !b) return; const pinchAround = this._aroundCenter ? null : a.add(b).div(2); @@ -52,12 +54,12 @@ class TwoTouchHandler { } - touchend(e: TouchEvent, points: Array) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._firstTwoTouches) return; const [idA, idB] = this._firstTwoTouches; - const a = getTouchById(e, points, idA); - const b = getTouchById(e, points, idB); + const a = getTouchById(mapTouches, points, idA); + const b = getTouchById(mapTouches, points, idB); if (a && b) return; if (this._active) DOM.suppressClick(); @@ -88,9 +90,9 @@ class TwoTouchHandler { } } -function getTouchById(e: TouchEvent, points: Array, identifier: number) { - for (let i = 0; i < e.targetTouches.length; i++) { - if (e.targetTouches[i].identifier === identifier) return points[i]; +function getTouchById(mapTouches: Array, points: Array, identifier: number) { + for (let i = 0; i < mapTouches.length; i++) { + if (mapTouches[i].identifier === identifier) return points[i]; } } diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index 1589e1d0f59..42325902491 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -49,10 +49,10 @@ export interface Handler { // Handlers can optionally implement these methods. // They are called with dom events whenever those dom evens are received. - +touchstart?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchmove?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchend?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchcancel?: (e: TouchEvent, points: Array) => HandlerResult | void; + +touchstart?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchmove?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchend?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchcancel?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; +mousedown?: (e: MouseEvent, point: Point) => HandlerResult | void; +mousemove?: (e: MouseEvent, point: Point) => HandlerResult | void; +mouseup?: (e: MouseEvent, point: Point) => HandlerResult | void; @@ -273,6 +273,17 @@ class HandlerManager { this.handleEvent(e, `${e.type}Window`); } + _getMapTouches(touches: TouchList) { + const mapTouches = []; + for (const t of touches) { + const target = ((t.target: any): Node); + if (this._el.contains(target)) { + mapTouches.push(t); + } + } + return ((mapTouches: any): TouchList); + } + handleEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { if (e.type === 'blur') { @@ -294,9 +305,8 @@ class HandlerManager { const eventsInProgress = {}; const activeHandlers = {}; - const points = e ? (e.targetTouches ? - DOM.touchPos(this._el, ((e: any): TouchEvent).targetTouches) : - DOM.mousePos(this._el, ((e: any): MouseEvent))) : null; + const mapTouches = e.touches ? this._getMapTouches(((e: any): TouchEvent).touches) : undefined; + const points = mapTouches ? DOM.touchPos(this._el, mapTouches) : DOM.mousePos(this._el, ((e: any): MouseEvent)); for (const {handlerName, handler, allowed} of this._handlers) { if (!handler.isEnabled()) continue; @@ -307,7 +317,7 @@ class HandlerManager { } else { if ((handler: any)[eventName || e.type]) { - data = (handler: any)[eventName || e.type](e, points); + data = (handler: any)[eventName || e.type](e, points, mapTouches); this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent); if (data && data.needsRenderFrame) { this._triggerRenderFrame(); diff --git a/test/unit/ui/handler/dblclick_zoom.test.js b/test/unit/ui/handler/dblclick_zoom.test.js index a3adb4a63f4..737c7b86cf6 100644 --- a/test/unit/ui/handler/dblclick_zoom.test.js +++ b/test/unit/ui/handler/dblclick_zoom.test.js @@ -12,10 +12,10 @@ function createMap(t) { function simulateDoubleTap(map, delay = 100) { const canvas = map.getCanvas(); return new Promise(resolve => { - simulate.touchstart(canvas, {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(canvas, {touches: [{target: canvas, clientX: 0, clientY: 0}]}); simulate.touchend(canvas); setTimeout(() => { - simulate.touchstart(canvas, {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(canvas, {touches: [{target: canvas, clientX: 0, clientY: 0}]}); simulate.touchend(canvas); map._renderTaskQueue.run(); resolve(); @@ -122,7 +122,7 @@ test('DoubleClickZoomHandler zooms on the second touchend event of a double tap' map.on('zoomstart', zoom); const canvas = map.getCanvas(); - const touchOptions = {targetTouches: [{clientX: 0.5, clientY: 0.5}]}; + const touchOptions = {touches: [{target: canvas, clientX: 0.5, clientY: 0.5}]}; simulate.touchstart(canvas, touchOptions); simulate.touchend(canvas); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 6f364ae3e8e..785efaf488e 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -84,6 +84,7 @@ test('DragPanHandler captures mousemove events during a mouse-triggered drag (re test('DragPanHandler fires dragstart, drag, and dragend events at appropriate times in response to a touch-triggered drag', (t) => { const map = createMap(t); + const target = map.getCanvas(); const dragstart = t.spy(); const drag = t.spy(); @@ -93,13 +94,13 @@ test('DragPanHandler fires dragstart, drag, and dragend events at appropriate ti map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 0); t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); @@ -157,14 +158,15 @@ test('DragPanHandler ends a mouse-triggered drag if the window blurs', (t) => { test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { const map = createMap(t); + const target = map.getCanvas(); const dragend = t.spy(); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.blur(window); @@ -428,6 +430,7 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the mo test('DragPanHandler does not begin a drag if preventDefault is called on the touchstart event', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.on('touchstart', e => e.preventDefault()); @@ -439,10 +442,10 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.touchend(map.getCanvas()); @@ -458,6 +461,7 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to test('DragPanHandler does not begin a drag if preventDefault is called on the touchstart event (delegated)', (t) => { const map = createMap(t); + const target = map.getCanvas(); t.stub(map, 'getLayer') .callsFake(() => true); @@ -476,10 +480,10 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.touchend(map.getCanvas()); diff --git a/test/unit/ui/handler/map_event.test.js b/test/unit/ui/handler/map_event.test.js index 5e41314192e..bcc2a5b769c 100644 --- a/test/unit/ui/handler/map_event.test.js +++ b/test/unit/ui/handler/map_event.test.js @@ -11,6 +11,7 @@ function createMap(t) { test('MapEvent handler fires touch events with correct values', (t) => { const map = createMap(t); + const target = map.getCanvas(); const touchstart = t.spy(); const touchmove = t.spy(); @@ -20,9 +21,9 @@ test('MapEvent handler fires touch events with correct values', (t) => { map.on('touchmove', touchmove); map.on('touchend', touchend); - const touchesStart = [{identifier: 1, clientX: 0, clientY: 50}]; - const touchesMove = [{identifier: 1, clientX: 0, clientY: 60}]; - const touchesEnd = [{identifier: 1, clientX: 0, clientY: 60}]; + const touchesStart = [{target, identifier: 1, clientX: 0, clientY: 50}]; + const touchesMove = [{target, identifier: 1, clientX: 0, clientY: 60}]; + const touchesEnd = [{target, identifier: 1, clientX: 0, clientY: 60}]; simulate.touchstart(map.getCanvas(), {touches: touchesStart, targetTouches: touchesStart}); t.equal(touchstart.callCount, 1); diff --git a/test/unit/ui/handler/touch_zoom_rotate.test.js b/test/unit/ui/handler/touch_zoom_rotate.test.js index a70ce438c4f..5028aed6c0c 100644 --- a/test/unit/ui/handler/touch_zoom_rotate.test.js +++ b/test/unit/ui/handler/touch_zoom_rotate.test.js @@ -1,6 +1,7 @@ import {test} from '../../../util/test'; import window from '../../../../src/util/window'; import Map from '../../../../src/ui/map'; +import Marker from '../../../../src/ui/marker'; import DOM from '../../../../src/util/dom'; import simulate from '../../../util/simulate_interaction'; @@ -11,6 +12,7 @@ function createMap(t) { test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at appropriate times in response to a pinch-zoom gesture', (t) => { const map = createMap(t); + const target = map.getCanvas(); const zoomstart = t.spy(); const zoom = t.spy(); @@ -22,25 +24,25 @@ test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at approp map.on('zoom', zoom); map.on('zoomend', zoomend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -50}, {identifier: 2, clientX: 0, clientY: 50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -50}, {target, identifier: 2, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 0); t.equal(zoom.callCount, 0); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -100}, {identifier: 2, clientX: 0, clientY: 100}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -100}, {target, identifier: 2, clientX: 0, clientY: 100}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 1); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -60}, {identifier: 2, clientX: 0, clientY: 60}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -60}, {target, identifier: 2, clientX: 0, clientY: 60}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 2); t.equal(zoomend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom @@ -55,6 +57,7 @@ test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at approp test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at appropriate times in response to a pinch-rotate gesture', (t) => { const map = createMap(t); + const target = map.getCanvas(); const rotatestart = t.spy(); const rotate = t.spy(); @@ -64,25 +67,25 @@ test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at map.on('rotate', rotate); map.on('rotateend', rotateend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -50}, {identifier: 1, clientX: 0, clientY: 50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 0); t.equal(rotate.callCount, 0); t.equal(rotateend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: -50, clientY: 0}, {identifier: 1, clientX: 50, clientY: 0}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: -50, clientY: 0}, {target, identifier: 1, clientX: 50, clientY: 0}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 1); t.equal(rotateend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -50}, {identifier: 1, clientX: 0, clientY: 50}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 2); t.equal(rotateend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 2); @@ -94,19 +97,20 @@ test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at test('TouchZoomRotateHandler does not begin a gesture if preventDefault is called on the touchstart event', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.on('touchstart', e => e.preventDefault()); const move = t.spy(); map.on('move', move); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}, {clientX: 5, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 5, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}, {clientX: 0, clientY: 5}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); t.equal(move.callCount, 0); @@ -117,6 +121,7 @@ test('TouchZoomRotateHandler does not begin a gesture if preventDefault is calle test('TouchZoomRotateHandler starts zoom immediately when rotation disabled', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.touchZoomRotate.disableRotation(); map.handlers._handlersById.tapZoom.disable(); @@ -128,25 +133,25 @@ test('TouchZoomRotateHandler starts zoom immediately when rotation disabled', (t map.on('zoom', zoom); map.on('zoomend', zoomend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 5}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 0); t.equal(zoom.callCount, 0); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 6}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 6}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 1); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 4}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 4}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 2); t.equal(zoomend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom t.equal(zoomstart.callCount, 2); @@ -169,3 +174,113 @@ test('TouchZoomRotateHandler adds css class used for disabling default touch beh t.ok(map.getCanvasContainer().classList.contains(className)); t.end(); }); + +test('TouchZoomRotateHandler zooms when touching two markers on the same map', (t) => { + const map = createMap(t); + + const marker1 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const marker2 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const target1 = marker1.getElement(); + const target2 = marker2.getElement(); + + const zoomstart = t.spy(); + const zoom = t.spy(); + const zoomend = t.spy(); + + map.handlers._handlersById.tapZoom.disable(); + map.touchPitch.disable(); + map.on('zoomstart', zoomstart); + map.on('zoom', zoom); + map.on('zoomend', zoomend); + + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}, {target: target2, identifier: 2, clientX: 0, clientY: 50}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -100}, {target: target2, identifier: 2, clientX: 0, clientY: 100}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 1); + t.equal(zoom.callCount, 1); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -60}, {target: target2, identifier: 2, clientX: 0, clientY: 60}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 1); + t.equal(zoom.callCount, 2); + t.equal(zoomend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: []}); + map._renderTaskQueue.run(); + + // incremented because inertia starts a second zoom + t.equal(zoomstart.callCount, 2); + map._renderTaskQueue.run(); + t.equal(zoom.callCount, 3); + t.equal(zoomend.callCount, 1); + + map.remove(); + t.end(); +}); + +test('TouchZoomRotateHandler does not zoom when touching an element not on the map', (t) => { + const map = createMap(t); + + const marker1 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const marker2 = new Marker() + .setLngLat([0, 0]); + + const target1 = marker1.getElement(); // on map + const target2 = marker2.getElement(); // not on map + + const zoomstart = t.spy(); + const zoom = t.spy(); + const zoomend = t.spy(); + + map.handlers._handlersById.tapZoom.disable(); + map.touchPitch.disable(); + map.dragPan.disable(); + map.on('zoomstart', zoomstart); + map.on('zoom', zoom); + map.on('zoomend', zoomend); + + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}, {target: target2, identifier: 2, clientX: 0, clientY: 50}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -100}, {target: target2, identifier: 2, clientX: 0, clientY: 100}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -60}, {target: target2, identifier: 2, clientX: 0, clientY: 60}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: []}); + map._renderTaskQueue.run(); + + // incremented because inertia starts a second zoom + t.equal(zoomstart.callCount, 0); + map._renderTaskQueue.run(); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + map.remove(); + t.end(); +}); + diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 6e312951bd7..dedddf15f67 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -1999,7 +1999,7 @@ test('Map', (t) => { const map = createMap(t, {interactive: true}); map.flyTo({center: [200, 0], duration: 100}); - simulate.touchstart(map.getCanvasContainer(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvasContainer(), {touches: [{target: map.getCanvas(), clientX: 0, clientY: 0}]}); t.equal(map.isEasing(), false); map.remove();