※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。

<前のページ
【React + TypeScript】GoogleMapReactコンポーネントを使う-Part2

【React + TypeScript】GoogleMapReactコンポーネントを使う-Part3

react web開発 フロント開発 Google Maps API GoogleAPI TypeScript

投稿日:2021年4月23日

このエントリーをはてなブックマークに追加
今回はGoogleMapReactコンポーネント上にマーカーを配置する方法について実践を交えて詳しく解説しています。

はじめに

今回作成する機能

今回はマップにマーカーを配置する機能を作って見ます。
この機能の作成に必要な知識はマーカーのコンポーネントとGoogleMapReactのonClickプロパティです。

まずはそこを簡単に解説しておきましょう。

マーカーのコンポーネント

GoogleMapReactコンポーネントにはマーカーを子要素として渡すことができます。子要素として渡すコンポーネントにはマーカーを配置する場所として緯度経度をlat : numberlng : numberで渡す必要があります。
子要素に渡したコンポーネントはGoogleMapReactコンポーネント上での描画が自動的に管理されます。

onClick()

GoogleMapReactコンポーネントにはonClickというプロパティに関数を渡すことでマップをクリックした場合の処理を追加することができます。
onClickに渡した関数にはマップのクリック時に以下のようにevent : GoogleMapReact.ClickEventValueを渡すように実装されていて、クリック位置の情報を取得できます。

google-map-reactのソースコード
[...]
_onClick = (...args) =>
    this.props.onClick &&
    !this.childMouseDownArgs_ &&
    new Date().getTime() - this.childMouseUpTime_ > K_IDLE_CLICK_TIMEOUT &&
    this.dragTime_ === 0 &&
    this.props.onClick(...args);

_onMapClick = (event) => {
    if (this.markersDispatcher_) {
      // support touch events and recalculate mouse position on click
      this._onMapMouseMove(event);
      const currTime = new Date().getTime();
      if (currTime - this.dragTime_ > K_IDLE_TIMEOUT) {
        if (this.mouse_) {
          this._onClick({
            ...this.mouse_,
            event,
          });
        }

        this.markersDispatcher_.emit('kON_CLICK', event);
      }
    }
  };

[...]

return (
      <div
        style={this.props.style}
        onMouseMove={this._onMapMouseMove}
        onMouseDownCapture={this._onMapMouseDownCapture}
        onClick={this._onMapClick}
      >
        <GoogleMapMap registerChild={this._registerChild} />
        {IS_REACT_16 && overlay && createPortal(this._renderPortal(), overlay)}

        {/* render markers before map load done */}
        {mapMarkerPrerender}
      </div>
    );

[...]

この記事について

この記事のコードは前回の記事の続きになっています。

コードは以下の環境で確認されました。

  • node version 10.19.0
  • npm version 6.14.4
  • google-map-react version 2.1.9
  • react version 17.0.1

実践

まずはマーカーの画像を管理するコンポーネントを作成します。

今回はscssモジュールを利用するのでまずそれを作成します。

./src/Marker.module.scss
.area_base {
  width: 3rem;
  height: 3rem;
  display: "block";
}

.area_in_map {
  @extend .area_base;
}

.area_mouse_trace {
  @extend .area_base;
  z-index: 1;
  position: absolute;
  pointer-events: none;
}

.area_display_none {
  @extend .area_base;
  display: none;
}

.img {
  width: 100%;
  height: 100%;
  filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.8));
}

ここではreactのsassモジュールを使っています。
コンポーネント側の実装は以下の様になります。

./src/Marker.tsx
import React from "react";
import logoSvg from "./logo.svg";
import styles from "./Marker.module.scss";

export interface Props {
  traceMouse: boolean;
  inMap: boolean;
  position?: {
    x: number;
    y: number;
  };
}

const Marker = (props: Props) => {
  let classNameArea: string;
  if (props.inMap) {
    classNameArea = styles.area_in_map;
  } else if (props.traceMouse) {
    classNameArea = styles.area_mouse_trace;
  } else {
    classNameArea = styles.area_display_none;
  }
  return (
    <div
      className={classNameArea}
      style={{
        left: props.position?.x,
        top: props.position?.y,
      }}
    >
      <img src={logoSvg} alt="map marker logo" className={styles.img} />
    </div>
  );
};

export default Marker;

ここで注目してほしいのはclassNameAreaです。
このclassNameAreaはコンポーネントのクラス名を格納する変数なのですが、propstraceMouseinMapの値によってクラス名が変わる様に処理を書いています。
今回作成する機能では以下のように3つの状態を表現するためにこのような実装にしました。

  • マウストラッキング時
  • 地図に配置した時
  • 非表示時

ちなみに画像はcreate-react-appのテンプレートに用意されているreactアイコンを使用しています。

次に地図上に表示するマーカーコンポーネントを作成します。マーカーコンポーネントは必ずlatlngのpropsを受け取る必要があるため上で作成したMarkerコンポーネントをラッピングします。

./src/MapMarker.tsx
import React from "react";
import Marker, { Props as MarkerProps } from "./Marker";

export interface Props {
  lat: number;
  lng: number;
}

/**
 * 地図のマーカー
 * @param props
 * @returns
 */
const MapMarker = (props: Props) => {
  return <Marker traceMouse={false} inMap={true} />;
};

export default MapMarker;

最後に素材をまとめるコンポーネントを作成します。
このコンポーネントではmouseTrackingNowというstateでマウスのトラッキングのON/OFFを制御しています。このstateはclickButton()関数でトグルされ、trueのときにはmousePosというstateがページ上のマウス位置をトラッキングします。
更にmouseTrackingNowtrueのときにマップをクリックすると、clickMap()関数によってマップ上にマーカーを配置できます。

./src/SampleComponent.tsx
import React, { useState } from "react";
import GoogleMapReact, { ChangeEventValue } from "google-map-react";
import MapMarker, { Props as MarkerProps } from "./MapMarker";
import Marker from "./Marker";
import styles from "./SampleComponent.module.scss";

/**
 * マウスのポジション
 */
interface MousePosition {
  x: number;
  y: number;
}

/**
 * Mapに使用するプロパティ
 */
interface MapProps {
  center: {
    lat: number;
    lng: number;
  };
  zoom: number;
}

/**
 * MapのPropsの初期値
 */
const initialMapProps: MapProps = {
  center: {
    lat: 35.39,
    lng: 139.44,
  },
  zoom: 18,
};

/**
 * 地図上のMarkerのプロパティ
 */
const initialMarkerProps: MarkerProps = {
  lat: 0,
  lng: 0,
};

/**
 * APIキー
 */
const API_KEY = "****************"; // TODO: 自分のキーをここに入力

/**
 * サンプルとして地図を表示するコンポーネント
 */
const SampleComponent = () => {
  // 地図のポジション
  const [mapProps, setMapProps] = useState<MapProps>(initialMapProps);

  // 地図上のマーカーポジション
  const [markerProps, setMarkerProps] = useState<MarkerProps>(
    initialMarkerProps
  );

  // マウスのトラッキングON/OFF
  const [mouseTrackingNow, setMouseTrackingNow] = useState<boolean>(false);

  // マウスのポジション
  const [mousePos, setMousePos] = useState<MousePosition>({ x: 0, y: 0 });

  /**
   * マウストラッキング時に、マウスポジションを更新する
   * @param event
   */
  const onMouseMove = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (mouseTrackingNow) {
      setMousePos({ x: event.clientX, y: event.clientY });
    }
  };

  /**
   *  マウストラッキングのON/OFFをトグルする
   */
  const clickButton = () => {
    setMouseTrackingNow(!mouseTrackingNow);
  };

  /**
   * マップをクリックしたときの処理
   * トラッキングがONの場合にはマーカーを置く
   * @param value
   */
  const clickMap = (value: GoogleMapReact.ClickEventValue) => {
    if (mouseTrackingNow) {
      setMarkerProps({
        lat: value.lat,
        lng: value.lng,
      });
      setMouseTrackingNow(false);
    }
  };

  /**
   * 描画
   */
  return (
    <>
      <Marker traceMouse={mouseTrackingNow} position={mousePos} inMap={false} />
      <div
        className={styles.area}
        onMouseMove={mouseTrackingNow ? onMouseMove : undefined}
      >
        <button onClick={clickButton}>
          マーカー{mouseTrackingNow ? "OFF" : "ON"}
        </button>
        <GoogleMapReact
          bootstrapURLKeys={{ key: API_KEY }}
          defaultCenter={initialMapProps.center}
          center={mapProps.center}
          defaultZoom={initialMapProps.zoom}
          zoom={mapProps.zoom}
          onClick={clickMap}
        >
          <MapMarker lat={markerProps?.lat} lng={markerProps?.lng} />
        </GoogleMapReact>
      </div>
    </>
  );
};

export default SampleComponent;

最後にscssモジュールを作成して完成です。

./src/SampleComponent.module.scss
.area {
  width: 100vw;
  height: 100vh;
}

動かして見ましょう。

ターミナル
$ npm run start
▲プレビュー

さいごに

今回紹介したpropの他にもいくつかの機能が実装してあるので、確認してみると良いでしょう。

このエントリーをはてなブックマークに追加

<前のページ
【React + TypeScript】GoogleMapReactコンポーネントを使う-Part2

関連記事

記事へのコメント