Add feature-level interactions to a map
This example demonstrates how to use addInteraction
to handle independent feature-level interactions. The example uses feature state to change the styling when a feature is clicked or hovered. When clicking a feature, it becomes selected and displays its properties in a card. Using mouseenter
and mouseleave
events, features are highlighted as the mouse moves over them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add feature-level interactions to a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.10.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.10.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
.map-overlay {
display: none;
font: 12px/20px sans-serif;
padding: 10px;
position: absolute;
right: 0;
top: 0;
width: 230px;
overflow: hidden;
white-space: nowrap;
}
.map-overlay-inner {
background: #fff;
padding: 10px;
border-radius: 3px;
}
</style>
<div id="map"></div>
<div class="map-overlay right" id="properties"></div>
<script>
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
center: [-98, 38.88],
maxZoom: 5,
minZoom: 1,
zoom: 3
});
const card = document.getElementById('properties');
const showCard = (feature) => {
card.innerHTML = `
`;
card.style.display = 'block';
};
map.on('style.load', () => {
map.addSource('airports', {
'type': 'vector',
'url': 'mapbox://mapbox.04w69w5j',
'promoteId': 'abbrev'
});
map.addLayer({
'id': 'airport',
'source': 'airports',
'source-layer': 'ne_10m_airports',
'type': 'circle',
'paint': {
'circle-color': [
'case',
['boolean', ['feature-state', 'selected'], false],
'#f00',
'#4264fb'
],
'circle-radius': [
'case',
['boolean', ['feature-state', 'selected'], false],
6,
['boolean', ['feature-state', 'highlight'], false],
6,
4
],
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
// Clicking on a feature will highlight it and display its properties in the card
let selectedFeature = null;
map.addInteraction('click', {
type: 'click',
target: { layerId: 'airport' },
handler: ({ feature }) => {
if (selectedFeature) {
map.setFeatureState(selectedFeature, { selected: false });
}
selectedFeature = feature;
map.setFeatureState(feature, { selected: true });
showCard(feature);
}
});
// Clicking on the map will deselect the selected feature
map.addInteraction('map-click', {
type: 'click',
handler: () => {
if (selectedFeature) {
map.setFeatureState(selectedFeature, { selected: false });
selectedFeature = null;
card.style.display = 'none';
}
}
});
// Hovering over a feature will highlight it
map.addInteraction('mouseenter', {
type: 'mouseenter',
target: { layerId: 'airport' },
handler: ({ feature }) => {
map.setFeatureState(feature, { highlight: true });
map.getCanvas().style.cursor = 'pointer';
}
});
// Moving the mouse away from a feature will remove the highlight
map.addInteraction('mouseleave', {
type: 'mouseleave',
target: { layerId: 'airport' },
handler: ({ feature }) => {
map.setFeatureState(feature, { highlight: false });
map.getCanvas().style.cursor = '';
return false;
}
});
});
</script>
</body>
</html>
このコードスニペットは、
YOUR_MAPBOX_ACCESS_TOKEN
をあなたのMapboxアカウントのアクセストークンに置き換えるまで、期待通りに動作しません。import React, { useState, useEffect, useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
const MapboxExample = () => {
const mapContainerRef = useRef(null);
const mapRef = useRef(null);
// State
const [selectedFeature, setSelectedFeature] = useState(null);
// Refs to track the latest state values
const selectedFeatureRef = useRef(null);
useEffect(() => {
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const map = (mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
center: [-98, 38.88],
maxZoom: 5,
minZoom: 1,
zoom: 3
}));
map.on('style.load', () => {
map.addSource('airports', {
type: 'vector',
url: 'mapbox://mapbox.04w69w5j',
promoteId: 'abbrev'
});
map.addLayer({
id: 'airport',
source: 'airports',
'source-layer': 'ne_10m_airports',
type: 'circle',
paint: {
'circle-color': [
'case',
['boolean', ['feature-state', 'selected'], false],
'#f00',
'#4264fb'
],
'circle-radius': [
'case',
['boolean', ['feature-state', 'selected'], false],
6,
['boolean', ['feature-state', 'highlight'], false],
6,
4
],
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
// Clicking on a feature will highlight it and display its properties in the card
map.addInteraction('click', {
type: 'click',
target: { layerId: 'airport' },
handler: ({ feature }) => {
if (selectedFeatureRef.current) {
map.setFeatureState(selectedFeatureRef.current, {
selected: false
});
}
map.setFeatureState(feature, { selected: true });
setSelectedFeature(feature);
}
});
// Clicking on the map will deselect the selected feature
map.addInteraction('map-click', {
type: 'click',
handler: () => {
if (selectedFeatureRef.current) {
map.setFeatureState(selectedFeatureRef.current, {
selected: false
});
setSelectedFeature(null);
}
}
});
// Hovering over a feature will highlight it
map.addInteraction('mouseenter', {
type: 'mouseenter',
target: { layerId: 'airport' },
handler: ({ feature }) => {
map.setFeatureState(feature, { highlight: true });
map.getCanvas().style.cursor = 'pointer';
}
});
// Moving the mouse away from a feature will remove the highlight
map.addInteraction('mouseleave', {
type: 'mouseleave',
target: { layerId: 'airport' },
handler: ({ feature }) => {
map.setFeatureState(feature, { highlight: false });
map.getCanvas().style.cursor = '';
return false;
}
});
});
return () => map.remove();
}, []);
// Sync Ref with State
useEffect(() => {
selectedFeatureRef.current = selectedFeature;
}, [selectedFeature]);
return (
<div style={{ position: 'relative', width: '100%', height: '100vh' }}>
<div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
<div
className="map-overlay"
style={{
position: 'absolute',
right: 0,
top: 0,
width: '230px',
padding: '10px',
color: '#1a2224',
fontSize: '12px',
lineHeight: '20px',
fontFamily: 'sans-serif'
}}
>
{selectedFeature && (
<div
className="map-overlay-inner"
style={{
background: '#fff',
padding: '10px',
borderRadius: '3px'
}}
>
<code>${selectedFeature.properties.name}</code>
<hr />
{Object.entries(selectedFeature.properties).map(([key, value]) => (
<li>
<b>{key}</b>: {value.toString()}
</li>
))}
</div>
)}
</div>
</div>
);
};
export default MapboxExample;
このコードスニペットは、
YOUR_MAPBOX_ACCESS_TOKEN
をあなたのMapboxアカウントのアクセストークンに置き換えるまで、期待通りに動作しません。このexampleは役に立ちましたか?