LoginSignup
6
6

More than 3 years have passed since last update.

Vue.js & TypeScript で Google Mapの自作コンポーネント作成

Last updated at Posted at 2019-10-29

背景

Vue.jsでGoogle Mapを表示する際に、既存のライブラリであるvue2-google-mapsを使う選択肢もありましたが、今後様々なカスタマイズをしていきたいと考えた場合に使い勝手が少し悪いため自作することにしました。

今回の実装では現状私が必要とする機能のみなので、必要な場合は追加で実装する必要があります。

実装

GoogleMap

Google Map表示用のコンポーネントです。

GoogleMap.vue
<template>
  <div>
    <div id="googleMap" :style="`height:${height}`"></div>
    <template v-if="google && map">
      <slot :google="google" :map="map" />
    </template>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import qs from 'query-string'

const params = {
  key: 'Google API key',
  libraries: 'geometry,drawing,places',
  callback: 'handleLoadGoogleMapsScript'
}

interface GoogleMapWindow extends Window {
  handleLoadGoogleMapsScript: Function
  google: any
}

declare const window: GoogleMapWindow

@Component
export default class GoogleMap extends Vue {
  @Prop() private zoom!: number
  @Prop() private center!: { lat: number; lng: number }
  @Prop({ default: '240px' }) private height!: string
  google: any = null
  map: any = null

  mounted() {
    this.loadGoogleMapsScript().then(google => {
      this.google = google
      this.initializeMap()
    })
  }

  loadGoogleMapsScript() {
    return new Promise((resolve, reject) => {
      if (window.google) {
        return resolve(window.google)
      }
      const script = document.createElement('script')
      script.src = `https://maps.googleapis.com/maps/api/js?${qs.stringify(
        params
      )}`
      const head = document.querySelector('head')
      if (!head) return reject(new Error('head node is undefined'))
      head.appendChild(script)
      window.handleLoadGoogleMapsScript = () => {
        resolve(window.google)
      }
      setTimeout(() => {
        if (!window.google) {
          reject(new Error('failed load google api'))
        }
      }, 5000)
    })
  }

  initializeMap() {
    const mapContainer = this.$el.querySelector('#googleMap')
    const { Map, MapTypeId } = this.google.maps
    this.map = new Map(mapContainer, {
      zoom: this.zoom,
      center: this.center,
      mapTypeId: MapTypeId.ROADMAP
    })
  }
}
</script>

GoogleMapMarker

Goggle Map上に配置するMarker用のコンポーネントです。
追加機能として、クリックされた際に情報を表示させます。

GoogleMapMarker.vue
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'

@Component
export default class GoogleMapMarker extends Vue {
  @Prop() private google!: any
  @Prop() private map!: any
  @Prop() private position!: { lat: number; lng: number }
  @Prop() private title!: string
  marker: any
  infoWindow: any

  mounted() {
    const { Marker } = this.google.maps
    this.marker = new Marker({
      position: this.position,
      map: this.map
    })
    this.infoWindow = new this.google.maps.InfoWindow({
      content: `<div>${this.title}<div>`
    })
    this.google.maps.event.addListener(this.marker, 'click', () => {
      this.infoWindow.open(this.map, this.marker)
    })
  }
}
</script>

使用例

<GoogleMap
  :zoom="17"
  :center="center"
  :height="240"
>
  <template slot-scope="scope">
    <GoogleMapMarker
      v-for="(marker, i) in markers"
      :key="i"
      :position="marker"
      :google="scope.google"
      :map="scope.map"
    />
  </template>
</GoogleMap>

表示例

配置したMarkerクリック後のスクリーンショットです。
スクリーンショット 2019-10-24 13.52.21.png

参考

6
6
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
6
6