LoginSignup
1
0

More than 1 year has passed since last update.

AngularでGoogleMap上のクラスター再描画が重くてハマった件

Posted at

GoogleMapに0個~1000個のマーカー設置を切り替えるのをマーカーの追加/削除で対応していたらクラスターの再描画が重くて使い物にならなかったのでその対応方法。

結論

  • マーカーの表示/消去はマーカーの追加/削除ではなくvisibleプロパティを使用する。

  • クラスターにはignoreHidden=trueを設定する。

環境

  • Angular: 12.2.0
  • @agm/core: 3.0.0-beta.0
  • @agm/markercluster: 3.0.0-beta.0
  • @google/markerclusterplus

修正概要

改善前

<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [minZoom]="minZoom" [maxZoom]="maxZoom">
  <agm-marker-cluster imagePath="(クラスタ画像パス)">
    <agm-marker *ngFor="let marker of markerList" [latitude]="marker.lat" [longitude]="marker.lng" iconUrl="(マーカー画像パス)"></agm-marker>
  </agm-marker-cluster>
</agm-map>

改善前は*ngForでマーカー一覧(markerList)を設定しており表示対象の項目が変わるたびに全マーカー一覧からfilter()で絞った結果をmarkerListに設定していた。

この場合markerListが再設定されるたびに<agm-marker>のDOMが再構築され、それをトリガーにGoogleMap上に実際に表示されているクラスターのDOMが再構築されるのだが<agm-marker>が構築/削除されるたびにクラスター描画が行われているのか15秒程度GoogleMapが固まりさらにクラスター表示も一時的に変に重なったりして非常に重い。
image.png

改善後

マーカー一覧(markerList)の再設定をしてはいけないことが分かったのでmarkerListには全マーカー一覧をまず設定しておく。
そのうえで表示するマーカーにはvisible=true、表示しないマーカーにはvisible=falseを設定し、クラスター側でもignoreHidden=trueを設定することでvisible=falseのマーカーをクラスター対象外にする。
※表示するマーカー一覧を変更するときはmarkerListの配列インスタンスを変えずにvisibleプロパティのみを書き換える。

<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [minZoom]="minZoom" [maxZoom]="maxZoom">
  <agm-marker-cluster imagePath="(クラスタ画像パス)" [ignoreHidden]="true">
    <agm-marker *ngFor="let marker of markerList" [latitude]="marker.lat" [longitude]="marker.lng" iconUrl="(マーカー画像パス)" [visible]="marker.visible"></agm-marker>
  </agm-marker-cluster>
</agm-map>

https://angular-maps.com/api-docs/agm-core/directives/agmmarker#visible
https://angular-maps.com/api-docs/markerclusterer/directives/agmmarkercluster#ignoreHidden

こうすることで<agm-marker>のDOM再構築がなくなるのでクラスターの再描画回数が減り問題ないレベルになった。
ただし、マーカーのvisibleを変更したらMarkerClusterer.repaint().もコールするようにと上記ドキュメントに記載されているが<agm-marker-cluster>ディレクティブのインスタンスをコンポーネント側でどうやって取得するのかがいまだ不明※。
そのため、クラスター画像の更新がされないのでZoom値を強制的に変えるなりして強制再描画を行っている。

※ClusterManagerをDIしてgetClustererInstance()で取得できそうだったが取得しても別のインスタンスっぽくてrepaint()しても再描画されなかったがやり方がよくなかったか。。
https://angular-maps.com/api-docs/markerclusterer/injectables/clustermanager#getClustererInstance

まあ、使用しているAgmMapライブラリは2年近くアップデートされていないのでこちらより@angular/google-mapsを使用したほうがよいとは思う。

以下のサイトは大変参考になりました。上記のライブラリを使っているようでrepaint()も実施できている模様。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0