LoginSignup
0
0

More than 3 years have passed since last update.

Vue.js で googleMap に overlay view を表示するコンポーネントを作る

Posted at

google.maps.OverlayViewを継承する用クラス

GoogleOverlay.ts
export class GoogleOverlay {
  lat: string | number
  lng: string | number
  drawable: boolean = true
  div: HTMLDivElement | null
  className: string = ''
  google: any
  innerHtml: string
  updateClass: (className: string) => void
  updateHtml: (html: string) => void
  active: () => void
  inactive: () => void
  // extend google.maps.OverlayView
  getProjection: any
  getPanes: any
  setMap: any
  draw: () => void
  remove: () => void

  constructor(
    google: any,
    map: any,
    lat: string | number,
    lng: string | number,
    innerHtml: string,
    className: string = '',
    events: { click?: () => void } = {}
  ) {
    this.lat = lat
    this.lng = lng
    this.div = null
    this.google = google
    this.innerHtml = innerHtml
    this.className = className

    this.remove = () => {
      if (!this.div) return
      ;(this.div.parentNode as Node).removeChild(this.div)
      this.div = null
    }

    this.draw = () => {
      this.remove()
      if (!this.drawable) return
      const projection = this.getProjection()
      if (!projection) return
      const point = projection.fromLatLngToDivPixel(
        new this.google.maps.LatLng(this.lat, this.lng)
      )

      this.div = document.createElement('div')
      this.div.className = this.className
      this.div.innerHTML = this.innerHtml
      this.div.style.left = point.x + 'px'
      this.div.style.top = point.y + 'px'

      const panes = this.getPanes()
      panes.overlayMouseTarget.appendChild(this.div)
      google.maps.event.addDomListener(this.div, 'click', () => {
        events.click && events.click()
      })
    }

    this.updateHtml = (html: string) => {
      this.innerHtml = html
      this.draw()
    }

    this.updateClass = (className: string) => {
      this.className = className
      this.draw()
    }

    this.active = () => {
      this.drawable = true
      this.draw()
    }

    this.inactive = () => {
      this.drawable = false
      this.remove()
    }

    this.setMap(map)
  }
}

GoogleMapにOverlayViewを表示する用のコンポーネント

GoogleMapOverlayView.vue
<template>
  <div v-show="false">
    <slot />
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { GoogleOverlay } from '../../lib/googleMap'

@Component
export default class GoogleMapOverlayView extends Vue {
  @Prop() private google!: any
  @Prop() private map!: any
  @Prop() private center!: { lat: number; lng: number }
  @Prop() private active!: boolean
  overlay: GoogleOverlay | null = null
  html: string = ''

  @Watch('active')
  setOrRemove() {
    if (this.active) {
      this.overlay ? this.overlay.active() : this.setOverlay()
      return
    }
    this.overlay && this.overlay.inactive()
  }

  mounted() {
    this.setOrRemove()
  }

  updated() {
    if (!this.overlay || !this.$el) return
    if (this.html === this.$el.innerHTML) return
    this.overlay.updateHtml(this.$el.innerHTML)
    this.html = this.$el.innerHTML
  }

  setOverlay() {
    GoogleOverlay.prototype = new this.google.maps.OverlayView()
    this.overlay = new GoogleOverlay(
      this.google,
      this.map,
      this.center.lat,
      this.center.lng,
      this.html,
      'google-map-overlay-view',
      { click: this.handleClick }
    )
  }

  handleClick() {
    this.$emit('click')
  }
}
</script>

使用例

<GoogleMap
  :zoom="17"
  :center="center"
  :height="240"
>
  <template slot-scope="scope">
    <GoogleMapOverlayView
      v-for="overlay in overlays"
      :key="overlay.key"
      :center="overlay.latLng"
      :google="scope.google"
      :map="scope.map"
      @click="handleSelectOverview(hotel)"
    >
      <div>overlayView</div>
    </GoogleMapOverlayView>
  </template>
</GoogleMap>
0
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
0
0