こんにちは。アーティサン株式会社の木戸です。
前回に引き続き、Angular で Google Maps を利用した時の、大量のマーカー表示によるユーザーエクスペリエンスの低下を回避する方法についてご紹介します。
前編では、マーカークラスタリングライブラリの Angular への導入、設定方法等、基本的な部分に触れました。
本記事では、より実践的な内容としてクラスターマーカーの表示・非表示の切り替え、マーカーアニメーション使用時の注意点を記載致します。
Angular でマーカークラスタリングライブラリ(js-markerclustererplus)を利用し、クラスターマーカーの表示・非表示の切り替えを行いたい方に向けた記事となります。
Google Maps マーカークラスタリングライブラリ
(js-markerclustererplus)とは
表示されている Google Maps を一定のサイズの正方形で区切り、各正方形内に含まれているマーカーを統合し、1 つのクラスターマーカーで表示するライブラリです。
マーカーを統合して表示する事によって、ユーザーエクスペリエンスを改善する事ができます。 また、大量のマーカーを一度に扱わずにすむため、処理負荷を軽減する事もできます。
環境
- Node: 14.17.0
- npm: 6.14.13
- Angular: 12.1.1
- @angular/google-maps: 12.1.1
- @googlemaps/markerclustererplus: 1.2.0
- TypeScript: 4.3.5
クラスターマーカーの表示・非表示の切り替え
クラスターマーカーを表示しているコンポーネント自体に表示切り替えの設定がないため、下記の実装手順で切り替えを行います。
今回の例ではマップのクリックイベントにマーカーの表示切り替えを行う関数を設定する形で実装します。
実装手順
- クラスターマーカーの ignoreHidden プロパティを true に設定
- クラスターマーカー内にある、各マーカーコンポーネントの visible プロパティを更新
- クラスターマーカーの repaint メソッドを呼び出す
クラスターマーカーに ignoreHidden を設定
ignoreHidden プロパティを true に設定します。(6 行目)
ignoreHidden とは
クラスターマーカー内にある、非表示のマーカーコンポーネント(visible プロパティが false)を無視するかどうかを設定します。
true に設定する事によって、visible プロパティが false のマーカーコンポーネントがクラスターマーカーに含まれなくなります。
<!-- sample.component.html -->
<google-map (mapClick)="toggleMarkers()">
<map-marker-clusterer
#markerClusterer="mapMarkerClusterer"
imagePath="assets/images/m"
[ignoreHidden]="true"
>
<ng-container *ngFor="let position of markerPositions">
<map-marker [position]="position"></map-marker>
</ng-container>
</map-marker-clusterer>
</google-map>
クラスタマーカー内にある、各マーカーコンポーネントの visible を更新
typescript ファイル内で@ViewChild を使用し、html ファイルにある map-marker-cluster コンポーネントを取得します。(10 行目)
クラスターマーカー内にある各マーカーコンポーネントの visible プロパティを更新し、表示切り替えを行います。(15, 16 行目)
// sample.component.ts
import { Component } from "@angular/core";
@Component({
selector: "app-sample",
templateUrl: "./sample.component.html",
styleUrls: ["./sample.component.scss"],
})
export class SampleComponent {
@ViewChild("markerClusterer") markerClusterer!: MapMarkerClusterer;
public markerPositions: google.maps.LatLngLiteral[] = [];
public toggleMarkers(): void {
const markers = this.markerClusterer?.markerClusterer?.getMarkers() ?? [];
for (const marker of markers) marker.setVisible(!marker.getVisible());
this.markerClusterer?.markerClusterer?.repaint();
}
}
クラスターマーカーの repaint メソッドを呼び出す
各マーカーコンポーネントの visible プロパティ更新後、画面表示の更新をするため、repaint メソッドを呼び出します。(17 行目)
全てのマーカーを非表示にする事によって、クラスターマーカーにマーカーが含まれなくなり、画面表示の更新後クラスターマーカー自体が表示されなくなります。
下図は「repaint メソッド呼び出し前」と「repaint メソッド呼び出し後」の画像になります。
// sample.component.ts
import { Component } from "@angular/core";
@Component({
selector: "app-sample",
templateUrl: "./sample.component.html",
styleUrls: ["./sample.component.scss"],
})
export class SampleComponent {
@ViewChild("markerClusterer") markerClusterer!: MapMarkerClusterer;
public markerPositions: google.maps.LatLngLiteral[] = [];
public toggleMarkers(): void {
const markers = this.markerClusterer?.markerClusterer?.getMarkers() ?? [];
for (const marker of markers) marker.setVisible(!marker.getVisible());
this.markerClusterer?.markerClusterer?.repaint();
}
}
マーカーアニメーション使用時の注意点
マーカーアニメーションを利用している場合に visible プロパティの変更や、クラスタリングライブラリを利用してクラスターマーカーとの表示切り替えを行うと、再表示時にアニメーションが再生されません。
そのため、下記のような形で明示的にアニメーションを再設定する必要があります。
マーカーアニメーションとは
マーカーコンポーネントの options.animation プロパティにgoogle.maps.Animation.BOUNCE等の値を代入することにより、マーカーをアニメーションさせる機能です。
google.maps.Animation.BOUNCEを代入した場合、マーカーが上下に動きます。
実装手順
- マーカーコンポーネントの visibleChanged イベント、クラスターマーカーコンポーネントの clusteringend イベントにアニメーションを再設定する関数を設定
- 設定した関数内で setAnimation メソッドを呼び出す
マーカーコンポーネントの visibleChanged イベント、クラスターマーカーコンポーネントの clusteringend イベントに関数を設定
visibleChanged イベント、clusteringend イベントにアニメーションを再設定する関数を呼び出す処理を設定します。(7、13 行目)
visible プロパティが変更される、またはクラスタリングの処理が終了した際に、設定した関数が呼び出されます。
<!-- sample.component.html -->
<google-map>
<map-marker-clusterer
#markerClusterer="mapMarkerClusterer"
imagePath="assets/images/m"
[ignoreHidden]="true"
(clusteringend)="setMarkersAnimation()"
>
<ng-container *ngFor="let position of markerPositions">
<map-marker
#marker="mapMarker"
[position]="position"
(visibleChanged)="setMarkerAnimation(marker)"
></map-marker>
</ng-container>
</map-marker-clusterer>
</google-map>
設定した関数内で setAnimation メソッドを呼び出す
typescript ファイル内で@ViewChild を使用し、html ファイルにある map-marker コンポーネント、map-marker-clusterer コンポーネントを取得します。(10、11 行目)
マーカーに対して setAnimation メソッドを呼び出し、アニメーションを再設定します。(16、22 行目)
// sample.component.ts
import { Component } from "@angular/core";
@Component({
selector: "app-sample",
templateUrl: "./sample.component.html",
styleUrls: ["./sample.component.scss"],
})
export class SampleComponent {
@ViewChild("markerClusterer") markerClusterer!: MapMarkerClusterer;
@ViewChild("mapMarker") marker!: MapMarker;
public markerPositions: google.maps.LatLngLiteral[] = [];
public setMarkerAnimation(marker: MapMarker): void {
this.marker.marker?.setAnimation(google.maps.Animation.BOUNCE);
}
public setMarkersAnimation(): void {
const markers = this.markerClusterer?.markerClusterer?.getMarkers() ?? [];
for (const marker of markers)
marker.setAnimation(google.maps.Animation.BOUNCE);
}
}
あとがき
本記事でご紹介した設定以外にクラスターマーカーのフォント等、表示面でも細かく設定することが可能です。 下記関連リンクのClusterIconStyleから確認できますので、ぜひ試してみて下さい。
関連リンク
- Angular で Google Maps マーカークラスタリングライブラリの利用(1)
- MapMarkerClusterer
- js-markerclustererplus
- Animation constants
- ClusterIconStyle
木戸裕貴
私は主にTypeScriptでのフロントエンド開発を担当しております。