UNPKG

maplibre-gl

Version:

BSD licensed community fork of mapbox-gl, a WebGL interactive maps library

429 lines (332 loc) 17.9 kB
import {describe, beforeEach, test, expect, vi} from 'vitest'; import {Map, type MapOptions} from '../map'; import {Marker} from '../marker'; import {DOM} from '../../util/dom'; import simulate from '../../../test/unit/lib/simulate_interaction'; import {beforeMapTest} from '../../util/test/util'; function createMap() { return new Map({container: DOM.create('div', '', window.document.body)} as any as MapOptions); } function createPinchZoomMap() { const map = createMap(); map.touchZoomRotate.disableRotation(); map.handlers._handlersById.tapZoom.disable(); map.touchPitch.disable(); return {map, target: map.getCanvas()}; } function startPinchGesture(map: Map, target: HTMLElement) { simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -50}, {target, identifier: 2, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); } function movePinchGesture(map: Map, target: HTMLElement, topY: number, bottomY: number) { simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: topY}, {target, identifier: 2, clientX: 0, clientY: bottomY}]}); map._renderTaskQueue.run(); } function endPinchGesture(map: Map) { simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); map._renderTaskQueue.run(); } beforeEach(() => { beforeMapTest(); }); describe('touch zoom rotate', () => { test('TwoFingersTouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at appropriate times in response to a pinch-zoom gesture', () => { const map = createMap(); const target = map.getCanvas(); const zoomstart = vi.fn(); const zoom = vi.fn(); const zoomend = vi.fn(); 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, identifier: 1, clientX: 0, clientY: -50}, {target, identifier: 2, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -100}, {target, identifier: 2, clientX: 0, clientY: 100}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(1); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -60}, {target, identifier: 2, clientX: 0, clientY: 60}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(2); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom expect(zoomstart).toHaveBeenCalledTimes(2); map._renderTaskQueue.run(); expect(zoom).toHaveBeenCalledTimes(3); expect(zoomend).toHaveBeenCalledTimes(1); map.remove(); }); test('TwoFingersTouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at appropriate times in response to a pinch-rotate gesture', () => { const map = createMap(); const target = map.getCanvas(); const rotatestart = vi.fn(); const rotate = vi.fn(); const rotateend = vi.fn(); map.on('rotatestart', rotatestart); map.on('rotate', rotate); map.on('rotateend', rotateend); simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); expect(rotatestart).toHaveBeenCalledTimes(0); expect(rotate).toHaveBeenCalledTimes(0); expect(rotateend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: -50, clientY: 0}, {target, identifier: 1, clientX: 50, clientY: 0}]}); map._renderTaskQueue.run(); expect(rotatestart).toHaveBeenCalledTimes(1); expect(rotate).toHaveBeenCalledTimes(1); expect(rotateend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); expect(rotatestart).toHaveBeenCalledTimes(1); expect(rotate).toHaveBeenCalledTimes(2); expect(rotateend).toHaveBeenCalledTimes(0); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); expect(rotatestart).toHaveBeenCalledTimes(1); expect(rotate).toHaveBeenCalledTimes(2); expect(rotateend).toHaveBeenCalledTimes(1); map.remove(); }); test('TwoFingersTouchZoomRotateHandler scales pinch zoom with setZoomRate', () => { const {map: defaultMap, target: defaultTarget} = createPinchZoomMap(); const defaultStartZoom = defaultMap.getZoom(); startPinchGesture(defaultMap, defaultTarget); movePinchGesture(defaultMap, defaultTarget, -100, 100); const defaultZoomDelta = defaultMap.getZoom() - defaultStartZoom; endPinchGesture(defaultMap); defaultMap.remove(); const {map: slowMap, target: slowTarget} = createPinchZoomMap(); slowMap.touchZoomRotate.setZoomRate(0.5); const slowStartZoom = slowMap.getZoom(); startPinchGesture(slowMap, slowTarget); movePinchGesture(slowMap, slowTarget, -100, 100); const slowZoomDelta = slowMap.getZoom() - slowStartZoom; endPinchGesture(slowMap); slowMap.remove(); expect(defaultZoomDelta).toBeGreaterThan(slowZoomDelta); expect(slowZoomDelta).toBeCloseTo(defaultZoomDelta * 0.5, 5); }); test('TwoFingersTouchZoomRotateHandler restores the default pinch zoom rate', () => { const {map: defaultMap, target: defaultTarget} = createPinchZoomMap(); const defaultStartZoom = defaultMap.getZoom(); startPinchGesture(defaultMap, defaultTarget); movePinchGesture(defaultMap, defaultTarget, -100, 100); const defaultZoomDelta = defaultMap.getZoom() - defaultStartZoom; endPinchGesture(defaultMap); defaultMap.remove(); const {map: restoredMap, target: restoredTarget} = createPinchZoomMap(); restoredMap.touchZoomRotate.setZoomRate(0.5); restoredMap.touchZoomRotate.setZoomRate(undefined); const restoredStartZoom = restoredMap.getZoom(); startPinchGesture(restoredMap, restoredTarget); movePinchGesture(restoredMap, restoredTarget, -100, 100); const restoredZoomDelta = restoredMap.getZoom() - restoredStartZoom; endPinchGesture(restoredMap); restoredMap.remove(); expect(restoredZoomDelta).toBeCloseTo(defaultZoomDelta, 5); }); test('TwoFingersTouchZoomRotateHandler scales pinch sensitivity with setZoomThreshold', () => { const {map: defaultMap, target: defaultTarget} = createPinchZoomMap(); const defaultZoomstart = vi.fn(); const defaultZoom = vi.fn(); defaultMap.on('zoomstart', defaultZoomstart); defaultMap.on('zoom', defaultZoom); startPinchGesture(defaultMap, defaultTarget); movePinchGesture(defaultMap, defaultTarget, -55, 55); endPinchGesture(defaultMap); defaultMap.remove(); const {map: insensitiveMap, target: insensitiveTarget} = createPinchZoomMap(); insensitiveMap.touchZoomRotate.setZoomThreshold(10); const insensitiveZoomstart = vi.fn(); const insensitiveZoom = vi.fn(); insensitiveMap.on('zoomstart', insensitiveZoomstart); insensitiveMap.on('zoom', insensitiveZoom); startPinchGesture(insensitiveMap, insensitiveTarget); movePinchGesture(insensitiveMap, insensitiveTarget, -55, 55); endPinchGesture(insensitiveMap); insensitiveMap.remove(); expect(defaultZoomstart).toHaveBeenCalledTimes(1); expect(defaultZoom).toHaveBeenCalledTimes(1); expect(insensitiveZoomstart).not.toHaveBeenCalled(); expect(insensitiveZoom).not.toHaveBeenCalled(); }); test('TwoFingersTouchZoomRotateHandler restores the default pinch zoom threshold', () => { const {map: defaultMap, target: defaultTarget} = createPinchZoomMap(); const defaultZoomstart = vi.fn(); const defaultZoom = vi.fn(); defaultMap.on('zoomstart', defaultZoomstart); defaultMap.on('zoom', defaultZoom); startPinchGesture(defaultMap, defaultTarget); movePinchGesture(defaultMap, defaultTarget, -55, 55); endPinchGesture(defaultMap); defaultMap.remove(); const {map: restoredMap, target: restoredTarget} = createPinchZoomMap(); restoredMap.touchZoomRotate.setZoomThreshold(10); restoredMap.touchZoomRotate.setZoomThreshold(undefined); const restoredZoomstart = vi.fn(); const restoredZoom = vi.fn(); restoredMap.on('zoomstart', restoredZoomstart); restoredMap.on('zoom', restoredZoom); startPinchGesture(restoredMap, restoredTarget); movePinchGesture(restoredMap, restoredTarget, -55, 55); endPinchGesture(restoredMap); restoredMap.remove(); expect(restoredZoomstart).toHaveBeenCalledTimes(defaultZoomstart.mock.calls.length); expect(restoredZoom).toHaveBeenCalledTimes(defaultZoom.mock.calls.length); }); test('TwoFingersTouchZoomRotateHandler does not begin a gesture if preventDefault is called on the touchstart event', () => { const map = createMap(); const target = map.getCanvas(); map.on('touchstart', e => { e.preventDefault(); }); const move = vi.fn(); map.on('move', move); simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 5, clientY: 0}]}); map._renderTaskQueue.run(); simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); expect(move).toHaveBeenCalledTimes(0); map.remove(); }); test('TwoFingersTouchZoomRotateHandler starts zoom immediately when rotation disabled', () => { const map = createMap(); const target = map.getCanvas(); map.touchZoomRotate.disableRotation(); map.handlers._handlersById.tapZoom.disable(); const zoomstart = vi.fn(); const zoom = vi.fn(); const zoomend = vi.fn(); map.on('zoomstart', zoomstart); map.on('zoom', zoom); map.on('zoomend', zoomend); simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 6}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(1); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 4}]}); map._renderTaskQueue.run(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(2); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom expect(zoomstart).toHaveBeenCalledTimes(2); map._renderTaskQueue.run(); expect(zoom).toHaveBeenCalledTimes(3); expect(zoomend).toHaveBeenCalledTimes(1); map.remove(); }); test('TwoFingersTouchZoomRotateHandler adds css class used for disabling default touch behavior in some browsers', () => { const map = createMap(); const className = 'maplibregl-touch-zoom-rotate'; expect(map.getCanvasContainer().classList.contains(className)).toBeTruthy(); map.touchZoomRotate.disable(); expect(map.getCanvasContainer().classList.contains(className)).toBeFalsy(); map.touchZoomRotate.enable(); expect(map.getCanvasContainer().classList.contains(className)).toBeTruthy(); }); test('TwoFingersTouchZoomRotateHandler zooms when touching two markers on the same map', () => { const map = createMap(); 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 = vi.fn(); const zoom = vi.fn(); const zoomend = vi.fn(); 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(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(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(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(1); expect(zoomend).toHaveBeenCalledTimes(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(); expect(zoomstart).toHaveBeenCalledTimes(1); expect(zoom).toHaveBeenCalledTimes(2); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom expect(zoomstart).toHaveBeenCalledTimes(2); map._renderTaskQueue.run(); expect(zoom).toHaveBeenCalledTimes(3); expect(zoomend).toHaveBeenCalledTimes(1); map.remove(); }); test('TwoFingersTouchZoomRotateHandler does not zoom when touching an element not on the map', () => { const map = createMap(); 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 = vi.fn(); const zoom = vi.fn(); const zoomend = vi.fn(); 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(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(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(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(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(); expect(zoomstart).toHaveBeenCalledTimes(0); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(0); simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom expect(zoomstart).toHaveBeenCalledTimes(0); map._renderTaskQueue.run(); expect(zoom).toHaveBeenCalledTimes(0); expect(zoomend).toHaveBeenCalledTimes(0); map.remove(); }); });