我已经完成了有关使用Vue和Google Maps的餐厅应用程序的项目。 一切正常,但是我仍然有一个无法通过标记纠正的错误。
当我在地图上移动且边界发生变化时,一些标记消失了。当我回到他们的位置时,它们不会一直出现。 如果我用光标快速移动,它们很有可能再次出现在地图上。
我在Chrome的Vue控制台中签到了,这里一切都很好。 即使在屏幕上并不总是可见,标记也会消失并出现在地图范围内。
API分为三个组件,我也使用VueX。
Google地图组件(创建地图)
<template>
<div class="main">
<div class="google-map" v-bind:id="mapName" ref="mainMap">
</div>
<!-- Tuto ici : https://itnext.io/new-unnamed-post-8da9cdbf5df3-->
<template v-if="Boolean(this.google) && Boolean(this.map)">
<slot :google="google" :map="map"></slot>
</template>
</div>
</template>
<script>
// Utilisation du plugin pour charger de manière asynchrone l'API
const GoogleMapsAPILoader = require('google-maps-api-loader');
export default {
name: 'google-map',
props: [
'name',
'defaultCenter'
],
data: function() {
return {
google: null,
mapName: this.name + "-map",
userCoord: {},
markers: [],
map: null,
bounds: null,
infoWindow: null,
}
},
// Petit plugin pour loader de manière asynchrone l'API Google et éviter des erreurs
async mounted() {
const google = await GoogleMapsAPILoader({
apiKey: 'APIKEY&libraries=places'
})
this.google = google
// Appel de InitMap, et des listeners
this.initMap();
this.addChangeBoundsListener();
this.openAddRestaurant();
},
methods: {
// Initialise la carte
initMap() {
// Pour y faire référence plus facilement
const element = this.$refs.mainMap
const options = {
center: this.defaultCenter,
zoom: 12,
}
this.map = new this.google.maps.Map(element, options);
this.infoWindow = new this.google.maps.InfoWindow;
// Emet google et map à MainMap
this.$emit('map-initialized', {
google: this.google,
map: this.map
})
},
addChangeBoundsListener() {
// Pour utiliser les bounds pour l'affichage des restaurants dans la liste
google.maps.event.addListener(this.map, 'bounds_changed', (event) => {
this.$emit('map-bounds-changed')
})
},
openAddRestaurant() {
// Emet l'event pour ajouter un restaurant au click sur la carte
google.maps.event.addListener(this.map, 'click', (event) => {
this.$emit('map-clicked', event);
})
},
}
};
</script>
<style scoped>
@media screen and (min-width: 446px) and (max-width: 1200px) {
.main {
margin-bottom: 1rem;
}
}
.google-map {
width: 100%;
height: 600px;
margin: 0 auto;
border: 2px solid #26A65B;
border-radius: 2rem;
}
</style>
Google标记组件(显示标记):
<template>
<div class="google-markers">
</div>
</template>
<script>
export default {
name: 'google-markers',
props: {
google: {
type: Object,
required: true
},
map: {
type: Object,
required: true
},
marker: {
type: Object,
required: true
}
},
data() {
return {
mapMarker: null
}
},
mounted() {
// Création des markers
this.mapMarker = new this.google.maps.Marker({
position: this.marker.position,
map: this.map,
marker: this.marker,
icon: this.getIconUrl(this.marker.type)
})
// Ajout du listener click sur icon ouvre composant ReadComments
this.mapMarker.addListener('click', () => {
if (this.marker.type !== 'user') {
this.$router.push({
path: `/read-comments/${this.marker.id}`
});
}
});
},
// Pour supprimer les markers avant de les redessiner
beforeDestroy() {
if (this.marker.type === 'user') console.log('je disparais');
this.mapMarker.setMap(null)
},
methods: {
// Dessiner les markers
getIconUrl(markerType) {
let icon
switch (this.marker.type) {
case 'restaurant':
icon = 'https://img.icons8.com/ios/50/000000/restaurant-table.png';
break;
case 'user':
console.log('user')
icon = 'https://img.icons8.com/color/48/000000/marker.png';
break;
default:
icon = 'https://img.icons8.com/ultraviolet/48/000000/record.png';
break;
}
return icon
}
},
computed: {
// Redessine les markers
refreshIcon() {
this.getIconUrl(this.marker.type);
}
}
}
</script>
<style scoped>
</style>
MainMap(使用其他两个组件,询问地理位置,创建标记,致电Google地方信息):
<template>
<google-map :center="customCenter" :defaultCenter="defaultCenter" @map-initialized="initialize" @map-bounds-changed="selectVisibleMarker" @map-clicked="openAddRestaurant">
<template slot-scope="{ google, map }">
<google-markers v-for="marker in markers" :marker="marker" :map="map" :google="google"></google-markers>
<google-markers v-if="userMarker !== {}" :marker="userMarker" :map="map" :google="google"></google-markers>
</template>
</google-map>
</template>
<script>
import GoogleMap from './GoogleMap'
import GoogleMarkers from './GoogleMarkers'
export default {
components: {
GoogleMap,
GoogleMarkers
},
data: function() {
return {
google: null,
mapName: this.name + "-map",
userCoord: {},
userMarker: {
type: 'user'
},
marker: null,
map: null,
bounds: null,
infoWindow: null,
position: {
lat: null,
lng: null
},
defaultCenter: {
lat: 48.842702,
lng: 2.328434
},
customCenter: {
lat: null,
lng: null
}
}
},
methods: {
// Vient de GoogleMap
initialize(data) {
this.map = data.map
this.google = data.google
this.askGeolocation()
},
// Demande si l'utilisateur accepte la géolocalisation, et recentre la carte sur sa position si acceptée.
askGeolocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
this.customCenter = pos
this.userCoord = pos
this.userMarker = {
...this.userMarker,
position: pos,
}
this.map.setCenter(this.customCenter)
this.setPlaces(pos);
}, () => {
this.handleLocationError(true, this.defaultCenter);
this.setPlaces(this.defaultCenter);
});
} else {
this.handleLocationError(false, this.defaultCenter);
this.setPlaces(this.defaultCenter);
}
},
handleLocationError(browserHasGeolocation, pos) {
this.map.setCenter(pos)
},
// selectVisibleRestaurant dépend du tri et de la zone d'affichage de la carte, et est utilisé par Map et List
selectVisibleMarker() {
this.$store.commit('setBoundsValue', this.map.getBounds())
this.$store.commit('selectVisibleRestaurant')
},
// ouvre le composant AddRestaurant avec lat et lng en query
openAddRestaurant(event) {
this.$router.push({
path: '/add-restaurant/',
query: {
lat: event.latLng.lat(),
lng: event.latLng.lng()
}
});
},
// Google Places
setPlaces(location) {
const service = new google.maps.places.PlacesService(this.map);
// Appel l'action getData du Store
this.$store.dispatch('getData', {
service,
location
})
}
},
computed: {
// Génère les markers
markers() {
const markersArray = [
...this.$store.getters.getRestaurantList.map((restaurant, index) => {
return {
id: restaurant.ID,
position: {
lat: parseFloat(restaurant.lat),
lng: parseFloat(restaurant.long),
},
type: 'restaurant'
}
})
]
if (this.userMarker !== {}) {
markersArray.push(this.userMarker)
}
return markersArray
}
}
}
</script>
商店:
import Vue from 'vue';
import Vuex from 'vuex';
import restaurantFactory from '../interfaces/restaurantFactory';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
restaurantList: [],
visibleRestaurant: [],
sortValue: [],
boundsValue: {}
},
getters: {
// Obtenir l'ID des restaurants
getRestaurantById: (state) => {
return (id) => {
const restaurantIndex = getRestaurantIndex(state.restaurantList, id);
console.log({
id,
restaurantIndex
});
return state.restaurantList[restaurantIndex];
};
},
getRestaurantList: state => {
return state.visibleRestaurant;
},
getSortValue: (state) => {
return state.sortValue;
},
getBoundsValue: (state) => {
return state.boundsValue;
},
// Calcul de la moyenne des notes données en commentaires
// getRestaurantAvgRating: (state) => {
// return (id) => {
// const restaurantIndex = getRestaurantIndex(state.restaurantList, id);
// const {
// ratings
// } = state.restaurantList[restaurantIndex];
// return computeAvgRatings(ratings)
// };
// }
},
mutations: {
setRestaurantList: (state, {
list
}) => {
state.restaurantList = list;
},
// Définit les restaurants à afficher en fonction des limites de la carte et du tri par moyenne
selectVisibleRestaurant(state) {
const bounds = state.boundsValue;
const range = state.sortValue;
state.visibleRestaurant = state.restaurantList.filter((restaurant) => {
let shouldBeVisible = true;
let isInMap = true;
let isInRange = true;
// Limites cartes
if (bounds) {
isInMap = restaurant.long >= bounds.ga.j && restaurant.long <= bounds.ga.l && restaurant.lat >= bounds.na.j && restaurant.lat <= bounds.na.l;
shouldBeVisible = shouldBeVisible && isInMap;
}
// Moyenne des notes
if (range && range.length === 2) {
isInRange = restaurant.avgRating >= range[0] && restaurant.avgRating <= range[1];
shouldBeVisible = shouldBeVisible && isInRange;
}
return shouldBeVisible;
});
},
setBoundsValue: (state, bounds) => {
state.boundsValue = bounds;
},
setSortValue: (state, range) => {
state.sortValue = range;
},
// Ajoute un restaurant en ajoutant automatiquement un champ avgRating et un ID (le dernier +1)
addRestaurant: (state, { newRestaurant }) => {
const ratings = newRestaurant.ratings || []
const restaurantToAdd = {
...newRestaurant,
ratings,
avgRating: computeAvgRatings(ratings),
ID: getLastId()
}
state.restaurantList.push(restaurantToAdd)
state.visibleRestaurant.push(restaurantToAdd)
function getLastId() {
const lastId = state.restaurantList.reduce((acc, restaurant) => {
if (acc < restaurant.ID) {
return restaurant.ID
}
return acc
}, 0)
return lastId + 1
}
},
// Ajoute un commentaire
addComment: (state, {
restaurantId,
comment
}) => {
const restaurantIndex = getRestaurantIndex(state.restaurantList, restaurantId);
state.restaurantList[restaurantIndex].ratings.push({
...comment
})
const restaurantRating = computeAvgRatings(state.restaurantList[restaurantIndex].ratings);
state.restaurantList[restaurantIndex].avgRating = restaurantRating;
}
},
// Fait appel à restaurantFactory et ajoute les restaurants de la liste JSON et de GooglePlaces
actions: {
getData: async function (context, { service, location }) {
const restaurantList = await restaurantFactory.getRestaurantList(service, location)
restaurantList.forEach((newRestaurant) => context.commit('addRestaurant', { newRestaurant }))
},
}
});
// Fonction helper pour getRestaurantById
function getRestaurantIndex(restaurantList, id) {
return restaurantList
.findIndex((restaurant) => restaurant.ID === parseInt(id))
}
// Fonction helper pour getRestaurantAvgRating
function computeAvgRatings (ratings) {
const avgRating = ratings.reduce((acc, rating) => {
return acc + (rating.stars / ratings.length);
}, 0);
return Math.round(avgRating);
}
在Google标记和MainMap中,标记是使用Computed处理的。 我无法确定问题出在哪里。
总结:如果标记的位置是否在边界之内,则动态删除和创建这些标记(对于Vue,在Chrome开发者控制台中可见),则它们不一定在地图上可见。
答案 0 :(得分:0)
如果在更改边界时重建了标记,则所有标记都必须先删除/删除,然后在地图上重置。
答案 1 :(得分:0)
这是解决此问题的方法:
<google-markers v-for="marker in markers" :key='marker' :marker="marker" :map="map" :google="google"></google-markers>
只需添加:key即可纠正此错误,并且标记现在可以正确显示在地图上。