Akryum/vue-googlemaps

Custom marker dynamically

thanhthaohoang opened this issue ยท 8 comments

Hi !
I would like to custom one marker on click or on hover. It could be by reducing its opacity or changing its icon.
I created my own component like this :

My component markerPlaces.vue

<template lang="html">
  <div>  </div>
</template>

<script>
import { MapElement } from 'vue-googlemaps'
import markerIcon from '~/assets/img/location-pointer.svg'
import { EventBus } from '~/plugins/event-bus.js'

// Those Vue props will update automatically
// (Two-way binding with .sync modifier)
const boundProps = [
	'animation',
	'clickable',
	'cursor',
]

// Events from Google Maps emitted as Vue events
const redirectedEvents = [
	'click',
	'rightclick',
]

export default {
  data () {
    return {
      // create new options object with origin property
      // allows reactivity on filtering
      opt: {
        position : {},
        icon: {}
      }
    }
  },
  mixins: [
    MapElement
  ],
  props: ['marker'],
  // When Google Maps is ready
  async googleMapsReady () {
      const options = await Object.assign(this.opt, {
          position: this.marker.position,
          icon: {
                url: markerIcon,
                scaledSize: new window.google.maps.Size(25, 32)
          }
    }, this.$props)

    options.map = this.$_map

    // Create Google Maps objects
    this.$_marker = new window.google.maps.Marker(options)
    // Bind the Vue props
    this.bindProps(this.$_marker, boundProps)
    // Emit the events from Google Maps
		this.redirectEvents(this.$_marker, redirectedEvents)
},

	beforeDestroy () {
    // Teardown
		if (this.$_marker) {
      // console.log('marker hidden', this.$_marker.marker);
			this.$_marker.setMap(null)
		}
	}
}
</script>

My vue

<template>
  <no-ssr>
    <section style="width: 100%" class="map">
        <googlemaps-map
          ref="mapRef"
          :center.sync="center"
          :zoom.sync="zoom"
          :options="mapOptions"
          @ready="ready"
        >
          <googlemaps-user-position
            :clickable="true"
            @click="centerOnUser"
            :positionStyle="positionStyle"
            @update:position="setUserPosition" />

          <markers-places
            v-for="(marker, index) of markers"
            :key="marker.id"
            :marker="marker"
            :class="{activeMarker: marker.id == selected}"
            @click="updateStateMarker(marker, index)"
          >
          </markers-places>
        </googlemaps-map>
      <div class="map__overlay"></div>
    </section>
  </no-ssr>
</template>

<script>
import mapStyle from '~/assets/json/mapStyle.json'
import * as VueGoogleMaps from 'vue-googlemaps'
import { EventBus } from '~/plugins/event-bus.js'
import markersPlaces from './markersPlaces.vue'
import markerUser from './markerUser.vue'

export default {
  name: 'mapComponent',
  data () {
   return {
    center: { lat: 47.218371, lng: -1.553621 },
    zoom: 14,
    mapOptions: {
      gestureHandling: 'greedy',
      draggable: true,
      zoomControl: true,
      mapTypeId: 'roadmap',
      styles: mapStyle
    },
    positionStyle: {
      path: 'M2.1999999999999993,11a8.8,8.8 0 1,0 17.6,0a8.8,8.8 0 1,0 -17.6,0',
			fillColor: '#2E86DE',
			fillOpacity: 1,
			scale: 1,
			strokeColor: '#8FBBE7',
			strokeWeight: 5
    },
    userPosition: {lat: 48.8445639, lng: 2.422224},
    selected: undefined
   }
  },
  components: {
    'markers-places': markersPlaces,
    'marker-user': markerUser
  },
  computed: {
    // userCoordinates() {
    //   return this.$store.getters['geolocation/getUserPosition']
    // },
    isLocated() {
      return this.$store.getters['geolocation/getIsLocated']
    },
    markers() {
      // console.log('markers', this.$store.getters['geolocation/getLocations']);
      return this.$store.getters['geolocation/getLocations']
    }
  },
  created () {
    EventBus.$on('i-got-swiped', index => {
      this.panToMarker(index)
    })
  },
  methods: {
    setUserPosition(position) {
      console.log('position', position);
      this.userPosition = position
      // push the last watch position in store
      this.$store.dispatch('geolocation/setUserPosition', this.userPosition )
    },
    ready () {
      this.$refs.mapRef.resize()
    },
    centerOnUser () {
      if (this.userPosition) {
        if (this.zoom <= 12) {
          this.zoom = Math.max(15, 12)
        }
        this.$refs.mapRef.panTo(this.userPosition)
      }
    },
    updateStateMarker (marker, index) {
      if (this.zoom <= 12) {
        this.zoom = Math.max(15, 12)
      }
      if(marker !== undefined) {
        this.selected = marker.id
        this.$refs.mapRef.panTo(marker.position)
      }
      // Send the event on a channel (i-got-clicked) with a payload (index of the clicked marker.)
      EventBus.$emit('i-got-clicked', index)
    },
    panToMarker (index) {
      if (this.zoom <= 12) {
        this.zoom = Math.max(15, 12)
      }
      if(this.markers[index] !== undefined) {
        this.center = this.markers[index].position
      }
    }
  }
}
</script>

hello how I'm starting to work with the modified markers, I have a question how do you actuate the position of the marker? @thanhthaohoang

Hi @ulises58 !
In my parent component, I send a prop called marker to my child component marker-places. That prop contains an object with a key position with coordinates values.
In the child component, I'm just attributing the sent position in the optionsconstant when the map is ready.
I don't know if it answers your question ๐Ÿ˜ฌ

Hello @thanhthaohoang , I think I did my question wrong, how to update the position of the marker after it was created for the first time?

<custom-marker
                v-for="(marker, index) of markers"
                :key="marker.id"
                :marker="marker"
                :position = "marker.position"
            >

Is this the correct way in which I must send to update the position?

I am using the same component markerPlaces code.

I am very new to this, thank you very much for your help. I know my question is very silly but I see that you have done it in a very good way.

@ulises58 To update the marker position, you can change the value of marker.position that you send as a value prop in your child component custom-marker. Did you try this before ?

@thanhthaohoang 0.0 Don't you mean something like this?

props: ['marker','position'],
       // When Google Maps is ready
       async googleMapsReady () {
           //https://pastebin.com/VYMCvS9m
           console.log(this.marker.position)
           const options = await Object.assign(this.opt, {
               position: this.marker.position,
               icon: {
                   url: 'https://maps.google.com/mapfiles/kml/shapes/parking_lot_maps.png',
                   scaledSize: new window.google.maps.Size(25, 32)
               }
           }, this.$props)

           options.map = this.$_map

           // Create Google Maps objects
           this.$_marker = new window.google.maps.Marker(options)
           // Bind the Vue props
           this.bindProps(this.$_marker, boundProps)
           // Emit the events from Google Maps
           this.redirectEvents(this.$_marker, redirectedEvents)
       },

const options = await Object.assign(this.opt, {
 position: this.marker.position,
  icon: {
     url: 'https://maps.google.com/mapfiles/kml/shapes/parking_lot_maps.png',
     scaledSize: new window.google.maps.Size(25, 32)
  }
}, this.$props)

In your code, position can take the value of the position prop that you stated earlier. Indeed, when you'll change the value of the position prop from your parent component, the position of your marker will be updated too I think.

Hi, thank you very much, I realized what my mistake was. Which is here

   const boundProps = [
        'animation',
        'clickable',
        'cursor',
        'position'
    ]

the position property was missing
thanks for your time @thanhthaohoang

@ulises58 Glad that I helped and that you found the solution on your own !