mirror of
https://github.com/visgl/react-map-gl.git
synced 2026-01-18 15:54:22 +00:00
Add/remove event handlers based on user setting (#308)
- Disable recognizers in `EventManager.off` if no more handlers are attached - Let `MapControls` manage its own events by passing in the `EventManager` instance - Use a new MapControls instance per map
This commit is contained in:
parent
4c0504a841
commit
e73cceea5f
@ -112,9 +112,7 @@ const defaultProps = Object.assign({}, StaticMap.defaultProps, MAPBOX_LIMITS, {
|
||||
clickRadius: 0,
|
||||
getCursor: getDefaultCursor,
|
||||
|
||||
visibilityConstraints: MAPBOX_LIMITS,
|
||||
|
||||
mapControls: new MapControls()
|
||||
visibilityConstraints: MAPBOX_LIMITS
|
||||
});
|
||||
|
||||
const childContextTypes = {
|
||||
@ -140,6 +138,10 @@ export default class InteractiveMap extends PureComponent {
|
||||
// Whether the cursor is over a clickable feature
|
||||
isHovering: false
|
||||
};
|
||||
|
||||
// If props.mapControls is not provided, fallback to default MapControls instance
|
||||
// Cannot use defaultProps here because it needs to be per map instance
|
||||
this._mapControls = props.mapControls || new MapControls();
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
@ -151,20 +153,22 @@ export default class InteractiveMap extends PureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
const {eventCanvas} = this.refs;
|
||||
const {mapControls} = this.props;
|
||||
|
||||
// Register event handlers defined by map controls
|
||||
const events = {};
|
||||
mapControls.events.forEach(eventName => {
|
||||
events[eventName] = this._handleEvent;
|
||||
});
|
||||
|
||||
const eventManager = new EventManager(eventCanvas, {events});
|
||||
const eventManager = new EventManager(eventCanvas);
|
||||
|
||||
// Register additional event handlers for click and hover
|
||||
eventManager.on('mousemove', this._onMouseMove);
|
||||
eventManager.on('click', this._onMouseClick);
|
||||
this._eventManager = eventManager;
|
||||
|
||||
this._mapControls.setOptions(Object.assign({}, this.props, {
|
||||
onStateChange: this._onInteractiveStateChange,
|
||||
eventManager
|
||||
}));
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this._mapControls.setOptions(nextProps);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -174,13 +178,6 @@ export default class InteractiveMap extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_handleEvent(event) {
|
||||
const controlOptions = Object.assign({}, this.props, {
|
||||
onStateChange: this._onInteractiveStateChange
|
||||
});
|
||||
return this.props.mapControls.handleEvent(event, controlOptions);
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this._map.getMap();
|
||||
}
|
||||
|
||||
@ -33,7 +33,6 @@ export const BASIC_EVENT_ALIASES = {
|
||||
* this block maps event names to the Recognizers required to detect the events.
|
||||
*/
|
||||
export const EVENT_RECOGNIZER_MAP = {
|
||||
click: 'tap',
|
||||
tap: 'tap',
|
||||
doubletap: 'doubletap',
|
||||
press: 'press',
|
||||
|
||||
@ -107,27 +107,32 @@ export default class EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/disable recognizer for the given event
|
||||
*/
|
||||
_toggleRecognizer(name, enabled) {
|
||||
const recognizer = this.manager.get(name);
|
||||
if (recognizer) {
|
||||
recognizer.set({enable: enabled});
|
||||
}
|
||||
this.wheelInput.toggleIfEventSupported(name, enabled);
|
||||
this.moveInput.toggleIfEventSupported(name, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the event registration for a single event + handler.
|
||||
*/
|
||||
_addEventHandler(event, handler) {
|
||||
// Special handling for gestural events.
|
||||
const recognizerEvent = EVENT_RECOGNIZER_MAP[event];
|
||||
if (recognizerEvent) {
|
||||
// Enable recognizer for this event.
|
||||
const recognizer = this.manager.get(recognizerEvent);
|
||||
recognizer.set({enable: true});
|
||||
}
|
||||
|
||||
this.wheelInput.enableIfEventSupported(event);
|
||||
this.moveInput.enableIfEventSupported(event);
|
||||
|
||||
const wrappedHandler = this._wrapEventHandler(event, handler);
|
||||
// Alias to a recognized gesture as necessary.
|
||||
const eventAlias = GESTURE_EVENT_ALIASES[event] || event;
|
||||
// Get recognizer for this event
|
||||
const recognizerName = EVENT_RECOGNIZER_MAP[eventAlias] || eventAlias;
|
||||
// Enable recognizer for this event.
|
||||
this._toggleRecognizer(recognizerName, true);
|
||||
|
||||
// Save wrapped handler
|
||||
this.eventHandlers.push({event, eventAlias, handler, wrappedHandler});
|
||||
this.eventHandlers.push({event, eventAlias, recognizerName, handler, wrappedHandler});
|
||||
|
||||
this.manager.on(eventAlias, wrappedHandler);
|
||||
}
|
||||
@ -136,6 +141,8 @@ export default class EventManager {
|
||||
* Process the event deregistration for a single event + handler.
|
||||
*/
|
||||
_removeEventHandler(event, handler) {
|
||||
let success = false;
|
||||
|
||||
// Find saved handler if any.
|
||||
for (let i = this.eventHandlers.length; i--;) {
|
||||
const entry = this.eventHandlers[i];
|
||||
@ -144,6 +151,21 @@ export default class EventManager {
|
||||
this.manager.off(entry.eventAlias, entry.wrappedHandler);
|
||||
// Delete saved handler
|
||||
this.eventHandlers.splice(i, 1);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Alias to a recognized gesture as necessary.
|
||||
const eventAlias = GESTURE_EVENT_ALIASES[event] || event;
|
||||
// Get recognizer for this event
|
||||
const recognizerName = EVENT_RECOGNIZER_MAP[eventAlias] || eventAlias;
|
||||
// Disable recognizer if no more handlers are attached to its events
|
||||
const isRecognizerUsed = this.eventHandlers.find(
|
||||
entry => entry.recognizerName === recognizerName
|
||||
);
|
||||
if (!isRecognizerUsed) {
|
||||
this._toggleRecognizer(recognizerName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup'];
|
||||
const MOVE_EVENT_TYPES = ['mousemove', 'pointermove'];
|
||||
const EVENT_TYPE = 'pointermove';
|
||||
|
||||
/**
|
||||
* Hammer.js swallows 'move' events (for pointer/touch/mouse)
|
||||
@ -35,9 +35,9 @@ export default class MoveInput {
|
||||
* Enable this input (begin processing events)
|
||||
* if the specified event type is among those handled by this input.
|
||||
*/
|
||||
enableIfEventSupported(eventType) {
|
||||
if (MOVE_EVENT_TYPES.indexOf(eventType) >= 0) {
|
||||
this.options.enable = true;
|
||||
toggleIfEventSupported(eventType, enabled) {
|
||||
if (EVENT_TYPE === eventType) {
|
||||
this.options.enable = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,12 +62,12 @@ export default class MoveInput {
|
||||
if (!this.pressed) {
|
||||
// Drag events are emitted by hammer already
|
||||
// we just need to emit the move event on hover
|
||||
MOVE_EVENT_TYPES.forEach(type => this.callback({
|
||||
type,
|
||||
this.callback({
|
||||
type: EVENT_TYPE,
|
||||
srcEvent: event,
|
||||
pointerType: 'mouse',
|
||||
target: event.target
|
||||
}));
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
|
||||
@ -53,9 +53,9 @@ export default class WheelInput {
|
||||
* Enable this input (begin processing events)
|
||||
* if the specified event type is among those handled by this input.
|
||||
*/
|
||||
enableIfEventSupported(eventType) {
|
||||
toggleIfEventSupported(eventType, enabled) {
|
||||
if (eventType === EVENT_TYPE) {
|
||||
this.options.enable = true;
|
||||
this.options.enable = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@ export default class WheelInput {
|
||||
if (!this.options.enable) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
let value = event.deltaY;
|
||||
if (window.WheelEvent) {
|
||||
|
||||
@ -25,16 +25,12 @@ const PITCH_MOUSE_THRESHOLD = 5;
|
||||
const PITCH_ACCEL = 1.2;
|
||||
const ZOOM_ACCEL = 0.01;
|
||||
|
||||
const SUBSCRIBED_EVENTS = [
|
||||
'panstart',
|
||||
'panmove',
|
||||
'panend',
|
||||
'pinchstart',
|
||||
'pinch',
|
||||
'pinchend',
|
||||
'doubletap',
|
||||
'wheel'
|
||||
];
|
||||
const EVENT_TYPES = {
|
||||
WHEEL: ['wheel'],
|
||||
PAN: ['panstart', 'panmove', 'panend'],
|
||||
PINCH: ['pinchstart', 'pinchmove', 'pinchend'],
|
||||
DOUBLE_TAP: ['doubletap']
|
||||
};
|
||||
|
||||
export default class MapControls {
|
||||
/**
|
||||
@ -42,19 +38,18 @@ export default class MapControls {
|
||||
* A class that handles events and updates mercator style viewport parameters
|
||||
*/
|
||||
constructor() {
|
||||
this.events = SUBSCRIBED_EVENTS;
|
||||
this._state = {
|
||||
isDragging: false
|
||||
};
|
||||
this.handleEvent = this.handleEvent.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for events
|
||||
* @param {hammer.Event} event
|
||||
*/
|
||||
handleEvent(event, options) {
|
||||
this.mapState = new MapState(Object.assign({}, options, this._state));
|
||||
this.setOptions(options);
|
||||
handleEvent(event) {
|
||||
this.mapState = new MapState(Object.assign({}, this.mapStateProps, this._state));
|
||||
|
||||
switch (event.type) {
|
||||
case 'panstart':
|
||||
@ -116,20 +111,37 @@ export default class MapControls {
|
||||
/**
|
||||
* Extract interactivity options
|
||||
*/
|
||||
setOptions({
|
||||
// TODO(deprecate): remove this when `onChangeViewport` gets deprecated
|
||||
onChangeViewport,
|
||||
onViewportChange,
|
||||
onStateChange,
|
||||
scrollZoom = true,
|
||||
dragPan = true,
|
||||
dragRotate = true,
|
||||
doubleClickZoom = true,
|
||||
touchZoomRotate = true
|
||||
}) {
|
||||
setOptions(options) {
|
||||
const {
|
||||
// TODO(deprecate): remove this when `onChangeViewport` gets deprecated
|
||||
onChangeViewport,
|
||||
onViewportChange,
|
||||
onStateChange = this.onStateChange,
|
||||
eventManager = this.eventManager,
|
||||
scrollZoom = true,
|
||||
dragPan = true,
|
||||
dragRotate = true,
|
||||
doubleClickZoom = true,
|
||||
touchZoomRotate = true
|
||||
} = options;
|
||||
|
||||
// TODO(deprecate): remove this check when `onChangeViewport` gets deprecated
|
||||
this.onViewportChange = onViewportChange || onChangeViewport;
|
||||
this.onStateChange = onStateChange;
|
||||
this.mapStateProps = options;
|
||||
if (this.eventManager !== eventManager) {
|
||||
// EventManager has changed
|
||||
this.eventManager = eventManager;
|
||||
this._events = {};
|
||||
}
|
||||
|
||||
// Register/unregister events
|
||||
this.toggleEvents(EVENT_TYPES.WHEEL, scrollZoom);
|
||||
this.toggleEvents(EVENT_TYPES.PAN, dragPan || dragRotate);
|
||||
this.toggleEvents(EVENT_TYPES.PINCH, touchZoomRotate);
|
||||
this.toggleEvents(EVENT_TYPES.DOUBLE_TAP, doubleClickZoom);
|
||||
|
||||
// Interaction toggles
|
||||
this.scrollZoom = scrollZoom;
|
||||
this.dragPan = dragPan;
|
||||
this.dragRotate = dragRotate;
|
||||
@ -137,6 +149,21 @@ export default class MapControls {
|
||||
this.touchZoomRotate = touchZoomRotate;
|
||||
}
|
||||
|
||||
toggleEvents(eventNames, enabled) {
|
||||
if (this.eventManager) {
|
||||
eventNames.forEach(eventName => {
|
||||
if (this._events[eventName] !== enabled) {
|
||||
this._events[eventName] = enabled;
|
||||
if (enabled) {
|
||||
this.eventManager.on(eventName, this.handleEvent);
|
||||
} else {
|
||||
this.eventManager.off(eventName, this.handleEvent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
// Default handler for the `panstart` event.
|
||||
_onPanStart(event) {
|
||||
@ -204,7 +231,6 @@ export default class MapControls {
|
||||
if (!this.scrollZoom) {
|
||||
return false;
|
||||
}
|
||||
event.srcEvent.preventDefault();
|
||||
|
||||
const pos = this.getCenter(event);
|
||||
const {delta} = event;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user