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>