👻

Mapbox Newsletter WEEKLY TIPSの解説 -「ドラッグ可能マーカーを作成」

2023/05/25に公開

はじめに

この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「ドラッグ可能マーカーを作成」についての解説です。また、Newsletterの購読はこちらからお申し込みいただけます。

コードを確認

まずExamplesのコードを見に行きましょう。

日本語サイト

英語サイト

基本的に同じコードですが、英語版はスタイルがMapbox Streets v12にアップグレードされているのでこちらを使用します。Mapbox Streets v11ではデフォルトのプロジェクションがWebメルカトルであるのに対し、Mapbox Streets v12ではGlobe(3D表示された地球)なので、印象がかなり異なります。

HTML/CSS

まずHTMLを見ていきましょう。

以下は地図を表示するエレメントです。

<div id="map"></div>

以下はマーカーをドラッグ&ドロップした際に左下に表示される軽度・緯度を表示するエレメントの定義です。

<pre id="coordinates" class="coordinates"></pre>

また、このエレメントは以下のスタイルが使用されています。

.coordinates {
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  position: absolute;
  bottom: 40px;
  left: 10px;
  padding: 5px 10px;
  margin: 0;
  font-size: 11px;
  line-height: 18px;
  border-radius: 3px;
  display: none;
}

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: [0, 0],
  zoom: 2
});

Markerの追加

Markerクラスをインスタンス化してマーカーを作成します。draggableオプションをtrueとすることでドラッグ可能なマーカーになります。setLngLatで表示位置を指定し、addToで地図に追加します。

const marker = new mapboxgl.Marker({
    draggable: true
  })
  .setLngLat([0, 0])
  .addTo(map);

Markerはドラッグに関するイベントを処理することができます。ここではdragendイベント(ドラッグ終了イベント)発生時に現在位置の表示を更新する処理を行っています。

function onDragEnd() {
  const lngLat = marker.getLngLat();
  coordinates.style.display = 'block';
  coordinates.innerHTML = `Longitude: ${lngLat.lng}<br />Latitude: ${lngLat.lat}`;
}
 
marker.on('dragend', onDragEnd);

他にもドラッグ開始イベント、ドラッグ中イベントが取得できます。詳細は以下をご参照ください。

Markerの実装方法を知る

以下の記事をご覧になった方は「すごく似ているなぁ」という感想を持たれたのではないでしょうか。

それもそのはず、「Mapの作成」までは一緒です。違いはマーカー(的なもの)をサークルレイヤーとして実装したか、Markerクラスを用いて実装したかだけです。Markerクラスはマウスイベント処理を内部で行うため、利用者側でドラッグの処理を書く必要はありません。そのため、このサンプルではコードが非常に簡潔になっています。それではMarkerがどのように実現されているかコードを見てみましょう。

マーカーの作成処理

Markerはデフォルトで水色のピンを表示しますが、このピンはHTMLエレメントとして実装されています。具体的にはこのコードが該当します。

if文の中の処理を一部抜き出してみました。this._elementにdivエレメントを作成し、更にその子要素としてSVGで表現したマーカーを追加しています。ちなみに、DOM.createDOM.createSVGここで定義されているヘルパー関数で、基本的にwindow.document.createElementでエレメントを作成しているだけです。

this._element = DOM.create('div');
...
const svg = DOM.createSVG('svg', {
    display: 'block',
    height: `${DEFAULT_HEIGHT * this._scale}px`,
    width: `${DEFAULT_WIDTH * this._scale}px`,
    viewBox: `0 0 ${DEFAULT_WIDTH} ${DEFAULT_HEIGHT}`
}, this._element);

マーカーの表示処理

つぎにadddToの処理を確認します。map.getCanvasContainer().appendChild(this._element);でMapが表示されているHTMLエレメントの子要素として先ほど作成したdivエレメントを追加しています。

つまり、MarkerクラスはマーカーをHTMLエレメントとして実装し、地図の上に重ねているということになります。

ということで、ドラッグ処理もMapのmoveイベントを受けてHTMLエレメントの場所を更新しています。

マーカーの見た目を変えてみる

それではマーカーの見た目を変更してみましょう。以下のようにマーカーとなるHTMLエレメントを作成し、Markerインスタンス作成時にelementオブションに設定するだけでOKです。

.marker {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #F84C4C;
}
const el = document.createElement("div");
el.className = "marker";

const marker = new mapboxgl.Marker({
  element: el,
  draggable: true
})
  .setLngLat([0, 0])
  .addTo(map);

結果は以下のとおりです。Create a draggable pointと同じ様な見た目にしてみました。任意のHTMLエレメントをマーカーとして使用できるので、動きのあるリッチなマーカーを作成することも可能です。

また、element以外のオプションを設定しない場合には以下のようにも記述できます。

const marker = new mapboxgl.Marker(el)
  .setLngLat([0, 0])
  .addTo(map);

まとめ

「ドラッグ可能マーカーを作成」はMarkerクラスを使用することで簡単にドラッグ可能なマーカーを作成しました。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion