Mapbox Newsletter WEEKLY TIPSの解説 -「ホバー時にポップアップを表示」
はじめに
この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「ホバー時にポップアップを表示」についての解説です。このサンプルではPopupおよびマウスイベントの使い方について例示しています。また、Newsletterの購読はこちらからお申し込みいただけます。
以下が本サンプルのデモです。
コードを確認
まずExamplesのコードを見に行きましょう。
日本語サイト
英語サイト
基本的に同じコードですが、英語版はスタイルがMapbox Streets v12にアップグレードされているのでこちらを使用します。Mapbox Streets v11ではデフォルトのプロジェクション(地図投影法)がWebメルカトルであるのに対し、Mapbox Streets v12ではGlobe(3D表示された地球)なので、印象がかなり異なります。また、英語版はMapbox GL JS v3が使用されています。
HTML/CSS
まずHTMLを見ていきましょう。
以下は地図を表示するエレメントです。
<div id="map"></div>
以下はポップアップのスタイルです。.mapboxgl-popup
というクラスはPopupのコンテナのdivを指します。
.mapboxgl-popup {
max-width: 400px;
font:
12px/20px 'Helvetica Neue',
Arial,
Helvetica,
sans-serif;
}
Mapの作成
次にJavaScriptのコードを見ていきます。以下のコードはいつも通り、Mapオブジェクトを作成しています。container
で地図を表示するHTMLエレメントのidを指定します。
const map = new mapboxgl.Map({
container: 'map',
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/streets-v12',
center: [-77.04, 38.907],
zoom: 11.15
});
ソース・レイヤーの作成
今回のデモは事前に複数の地点を登録し、そこにマウスカーソルを合わせるとポップアップが表示されます。そのため、複数地点をあらかじめ作成しておきます。ソースとレイヤーはスタイルが読み込まれた後に作成する必要があるため、map.on('load', () => { /* ここ */})
の部分に処理を書きます。
ソースの作成
Map#addSource
でソースを作成します。第1引数が任意のソースID、第2引数がソースになります。ここではGeoJSONをソースとして使用しています。FeatureCollection
を用いて、複数のPointデータを登録しています。
map.addSource('places', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {
'description':
'<strong>Make it Mount Pleasant</strong><p>Make it Mount Pleasant is a handmade and vintage market and afternoon of live entertainment and kids activities. 12:00-6:00 p.m.</p>'
},
'geometry': {
'type': 'Point',
'coordinates': [-77.038659, 38.931567]
}
},
...中略...
]
}
});
レイヤーの作成
Map#addLayer
でレイヤーを作成します。ここではCircleレイヤーを作成しています。また、source
は先程のソースのIDを指定します。
map.addLayer({
'id': 'places',
'type': 'circle',
'source': 'places',
'paint': {
'circle-color': '#4264fb',
'circle-radius': 6,
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
Popupの作成
次に、Popup
をあらかじめ作成しています。closeButton: false
で右上の✗ボタンを非表示、closeOnClick: false
でクリックでPopupを閉じる挙動を防止しています。
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
ホバー時の動作
マウスカーソルがCircleレイヤーの地点上に来たときにPopupを表示するのが目標です。そこでここではmouseenter
イベントを使用しています。map.on('mouseenter', レイヤーID, () => { /* ここ */})
のように使用します。第2引数に監視対象とするレイヤーのIDを指定します。マウスカーソルがそのレイヤーの表示領域に入ると「ここ」の処理が発火します。
処理内容を確認します。
まず、マウスカーソルの形状を変えています。
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';
コールバック関数の引数e
はMapMouseEvent
です。その中のfeatures
はイベントが起きたときのそのレイヤーのフィーチャーが格納されています。そこで、座標とプロパティを取得しています。
// Copy coordinates array.
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;
以下の処理はプロジェクション(地図投影法)がmercator
のときには必要な処理です。
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
mercator
では以下のように横方向に地図が繰り返し表示されますが、上記処理がないと右のCircleにマースカーソルを合わせたときに左側にPopupが出てしまいます。
ただし、Mapbox Streets v12ではプロジェクション(地図投影法)はglobe
がデフォルトであり、この処理は不要です。そこで、サンプルコードからも削除することが検討されています。
最後にPopupの表示する座標、内容を設定してMapオブジェクトにaddTo
することで表示されます。
// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(coordinates).setHTML(description).addTo(map);
マウスアウト時の動作
マウスカーソルがCircleレイヤーから外れたときはmouseleave
イベントを使用しています。マウスカーソルをデフォルトに戻し、Popup#rempve
でPopupを削除します。
map.on('mouseleave', 'places', () => {
map.getCanvas().style.cursor = '';
popup.remove();
});
まとめ
PopupはMarkerといっしょに使うことが多いですが、単独でも使用できることがわかりました。また、マウスイベントの扱い方についても確認しました。
Discussion