Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6fb78a8
Setting up the basic framework of the elements, next we need to add t…
ewagstaff May 11, 2022
ea5a574
We are correctly detecting when meta key is held, now actually let th…
ewagstaff May 11, 2022
514b975
Scrollwheel is done, testing 2 finger
ewagstaff May 13, 2022
e445463
Removed all logs
ewagstaff May 13, 2022
9d7996f
We don't need the easing function
ewagstaff May 13, 2022
9781c16
This scrolls correctly after the first press, let's tighten it up more
ewagstaff May 16, 2022
04436bd
Better handling for inline map complete
ewagstaff May 16, 2022
4d00826
Remove the inline map testing
ewagstaff May 16, 2022
0bb2f9b
Satisfy the linter
ewagstaff May 17, 2022
76ac63c
Fixing all remaining linter issues
ewagstaff May 17, 2022
0f20225
CSS lint fixes
ewagstaff May 17, 2022
0eaa726
Sigh more lint
ewagstaff May 17, 2022
3a32fad
Adding a newline at the end of css (lint)
ewagstaff May 17, 2022
56780a7
Added a 3-finger requirement for pitch change with coop gestures enabled
ewagstaff May 21, 2022
f3e8cb3
Handling ctrl key on PC instead of meta (which is actually the window…
ewagstaff Jun 17, 2022
b73070b
Added tests for coop gestures
ewagstaff Jun 19, 2022
d0a3739
Added a mobile breakpoint for 2 finger message in addition to the hov…
ewagstaff Jun 19, 2022
c0912bd
Updated changelog
ewagstaff Jun 19, 2022
f89ae01
Added option to customize the help text for gestures
ewagstaff Jun 29, 2022
df73932
Added GestureOptions type and reconfigured the type settings
ewagstaff Jun 30, 2022
86f08e6
Safely checking if var is not boolean before using properties
ewagstaff Jun 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

### ✨ Features and improvements

- *...Add new stuff here...*
- Added `collaborativeGestures` option when instantiating map to prevent inadvertent scrolling/panning when navigating a page where map is embedded inline (#234)

### 🐞 Bug fixes

Expand Down
38 changes: 38 additions & 0 deletions src/css/maplibre-gl.css
Original file line number Diff line number Diff line change
Expand Up @@ -917,3 +917,41 @@ a.mapboxgl-ctrl-logo.mapboxgl-compact {
border: 2px dotted #202020;
opacity: 0.5;
}

.maplibregl-cooperative-gesture-screen {
background: rgba(0 0 0 / 40%);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
color: white;
padding: 1rem;
font-size: 1.4em;
line-height: 1.2;
opacity: 0;
pointer-events: none;
transition: opacity 1s ease 1s;
}

.maplibregl-cooperative-gesture-screen.maplibregl-show {
opacity: 1;
transition: opacity 0.05s;
}

.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message {
display: none;
}

@media (hover: none), (max-width: 480px) {
.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message {
display: none;
}

.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message {
display: block;
}
}
167 changes: 167 additions & 0 deletions src/ui/handler/cooperative_gestures.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import browser from '../../util/browser';
import Map from '../map';
import DOM from '../../util/dom';
import simulate from '../../../test/unit/lib/simulate_interaction';
import {setMatchMedia, setPerformance, setWebGlContext} from '../../util/test/util';

function createMap() {
return new Map({
container: DOM.create('div', '', window.document.body),
style: {
'version': 8,
'sources': {},
'layers': []
},
cooperativeGestures: true
});
}

beforeEach(() => {
setPerformance();
setWebGlContext();
setMatchMedia();
});

describe('CoopGesturesHandler', () => {

test('Does not zoom on wheel if no key is down', () => {
const browserNow = jest.spyOn(browser, 'now');
let now = 1555555555555;
browserNow.mockReturnValue(now);

const map = createMap();
map._renderTaskQueue.run();

const startZoom = map.getZoom();
// simulate a single 'wheel' event
simulate.wheel(map.getCanvas(), {type: 'wheel', deltaY: -simulate.magicWheelZoomDelta});
map._renderTaskQueue.run();

now += 400;
browserNow.mockReturnValue(now);
map._renderTaskQueue.run();

const endZoom = map.getZoom();
expect(endZoom).toBeCloseTo(startZoom);

map.remove();
});

test('Zooms on wheel if control key is down', () => {
// NOTE: This should pass regardless of whether cooperativeGestures is enabled or not
const browserNow = jest.spyOn(browser, 'now');
let now = 1555555555555;
browserNow.mockReturnValue(now);

const map = createMap();
map._renderTaskQueue.run();

simulate.keydown(document, {type: 'keydown', key: 'Control'});
map._renderTaskQueue.run();

const startZoom = map.getZoom();
// simulate a single 'wheel' event
simulate.wheel(map.getCanvas(), {type: 'wheel', deltaY: -simulate.magicWheelZoomDelta});
map._renderTaskQueue.run();

now += 400;
browserNow.mockReturnValue(now);
map._renderTaskQueue.run();

const endZoom = map.getZoom();
expect(endZoom - startZoom).toBeCloseTo(0.0285, 3);

map.remove();
});

test('Does not pan on touchmove with a single touch', () => {
const map = createMap();
const target = map.getCanvas();
const startCenter = map.getCenter();
map._renderTaskQueue.run();

const dragstart = jest.fn();
const drag = jest.fn();
const dragend = jest.fn();

map.on('dragstart', dragstart);
map.on('drag', drag);
map.on('dragend', dragend);

simulate.touchstart(target, {touches: [{target, clientX: 0, clientY: 0}]});
map._renderTaskQueue.run();

simulate.touchmove(target, {touches: [{target, clientX: 10, clientY: 10}]});
map._renderTaskQueue.run();

simulate.touchend(target);
map._renderTaskQueue.run();

const endCenter = map.getCenter();
expect(endCenter.lng).toEqual(startCenter.lng);
expect(endCenter.lat).toEqual(startCenter.lat);

map.remove();
});

test('Does pan on touchmove with a double touch', () => {
// NOTE: This should pass regardless of whether cooperativeGestures is enabled or not
const map = createMap();
const target = map.getCanvas();
const startCenter = map.getCenter();
map._renderTaskQueue.run();

const dragstart = jest.fn();
const drag = jest.fn();
const dragend = jest.fn();

map.on('dragstart', dragstart);
map.on('drag', drag);
map.on('dragend', dragend);

simulate.touchstart(target, {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 1, clientY: 1}]});
map._renderTaskQueue.run();

simulate.touchmove(target, {touches: [{target, clientX: 10, clientY: 10}, {target, clientX: 11, clientY: 11}]});
map._renderTaskQueue.run();

simulate.touchend(target);
map._renderTaskQueue.run();

const endCenter = map.getCenter();
expect(endCenter.lng).toBeGreaterThan(startCenter.lng);
expect(endCenter.lat).toBeGreaterThan(startCenter.lat);

map.remove();
});

test('Drag pitch works with 3 fingers', () => {
// NOTE: This should pass regardless of whether cooperativeGestures is enabled or not
const map = createMap();
const target = map.getCanvas();
const startPitch = map.getPitch();
map._renderTaskQueue.run();

const dragstart = jest.fn();
const drag = jest.fn();
const dragend = jest.fn();

map.on('dragstart', dragstart);
map.on('drag', drag);
map.on('dragend', dragend);

simulate.touchstart(target, {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 1, clientY: 1}, {target, clientX: 2, clientY: 2}]});
map._renderTaskQueue.run();

simulate.touchmove(target, {touches: [{target, clientX: 0, clientY: -10}, {target, clientX: 1, clientY: -11}, {target, clientX: 2, clientY: -12}]});
map._renderTaskQueue.run();

simulate.touchend(target);
map._renderTaskQueue.run();

const endPitch = map.getPitch();
expect(endPitch).toBeGreaterThan(startPitch);

map.remove();
});
});
7 changes: 7 additions & 0 deletions src/ui/handler/scroll_zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ class ScrollZoomHandler {

wheel(e: WheelEvent) {
if (!this.isEnabled()) return;
if (this._map._cooperativeGestures) {
if (this._map._metaPress) {
e.preventDefault();
} else {
return;
}
}
let value = e.deltaMode === WheelEvent.DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY;
const now = browser.now(),
timeDelta = now - (this._lastWheelEventTime || 0);
Expand Down
24 changes: 22 additions & 2 deletions src/ui/handler/touch_pan.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Point from '@mapbox/point-geometry';
import {indexTouches} from './handler_util';
import type Map from '../map';
import {GestureOptions} from '../map';

export default class TouchPanHandler {

Expand All @@ -11,26 +13,44 @@ export default class TouchPanHandler {
_minTouches: number;
_clickTolerance: number;
_sum: Point;
_map: Map;
_cancelCooperativeMessage: boolean;

constructor(options: {
clickTolerance: number;
}) {
this._minTouches = 1;
cooperativeGestures: boolean | GestureOptions;
}, map: Map) {
this._minTouches = options.cooperativeGestures ? 2 : 1;
this._clickTolerance = options.clickTolerance || 1;
this._map = map;
this.reset();
}

reset() {
this._active = false;
this._touches = {};
this._sum = new Point(0, 0);

// Put a delay on the cooperative gesture message so it's less twitchy
setTimeout(() => {
this._cancelCooperativeMessage = false;
}, 200);
}

touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
return this._calculateTransform(e, points, mapTouches);
}

touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
if (this._map._cooperativeGestures) {
if (this._minTouches === 2 && mapTouches.length < 2 && !this._cancelCooperativeMessage) {
// If coop gesture enabled, show panning info to user
this._map._onCooperativeGesture(e, false, mapTouches.length);
} else if (!this._cancelCooperativeMessage) {
// If user is successfully navigating, we don't need this warning until the touch resets
this._cancelCooperativeMessage = true;
}
}
if (!this._active || mapTouches.length < this._minTouches) return;
e.preventDefault();
return this._calculateTransform(e, points, mapTouches);
Expand Down
19 changes: 18 additions & 1 deletion src/ui/handler/touch_zoom_rotate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Point from '@mapbox/point-geometry';
import DOM from '../../util/dom';
import type Map from '../map';

class TwoTouchHandler {

Expand All @@ -23,7 +24,6 @@ class TwoTouchHandler {
_move(points: [Point, Point], pinchAround: Point, e: TouchEvent) { return {}; } //eslint-disable-line

touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
//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;

Expand Down Expand Up @@ -203,6 +203,13 @@ export class TouchPitchHandler extends TwoTouchHandler {
_valid: boolean | void;
_firstMove: number;
_lastPoints: [Point, Point];
_map: Map;
_currentTouchCount: number;

constructor(map: Map) {
super();
this._map = map;
}

reset() {
super.reset();
Expand All @@ -211,6 +218,11 @@ export class TouchPitchHandler extends TwoTouchHandler {
delete this._lastPoints;
}

touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
super.touchstart(e, points, mapTouches);
this._currentTouchCount = mapTouches.length;
}

_start(points: [Point, Point]) {
this._lastPoints = points;
if (isVertical(points[0].sub(points[1]))) {
Expand All @@ -221,6 +233,11 @@ export class TouchPitchHandler extends TwoTouchHandler {
}

_move(points: [Point, Point], center: Point, e: TouchEvent) {
// If cooperative gestures is enabled, we need a 3-finger minimum for this gesture to register
if (this._map._cooperativeGestures && this._currentTouchCount < 3) {
return;
}

const vectorA = points[0].sub(this._lastPoints[0]);
const vectorB = points[1].sub(this._lastPoints[1]);

Expand Down
4 changes: 2 additions & 2 deletions src/ui/handler_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class HandlerManager {
const tapDragZoom = new TapDragZoomHandler();
this._add('tapDragZoom', tapDragZoom);

const touchPitch = map.touchPitch = new TouchPitchHandler();
const touchPitch = map.touchPitch = new TouchPitchHandler(map);
this._add('touchPitch', touchPitch);

const mouseRotate = new MouseRotateHandler(options);
Expand All @@ -203,7 +203,7 @@ class HandlerManager {
this._add('mousePitch', mousePitch, ['mouseRotate']);

const mousePan = new MousePanHandler(options);
const touchPan = new TouchPanHandler(options);
const touchPan = new TouchPanHandler(options, map);
map.dragPan = new DragPanHandler(el, mousePan, touchPan);
this._add('mousePan', mousePan);
this._add('touchPan', touchPan, ['touchZoom', 'touchRotate']);
Expand Down
Loading