±¸±Û¸Ê(Google Map) API¿¡¼ Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker) ±¸ÇöÇÏ´Â ¹æ¹ý
[ÀÚ¹Ù½ºÅ©¸³Æ®/Javascript] ±¸±Û¸Ê(Google Map) API¿¡¼ Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker) ±¸ÇöÇÏ´Â ¹æ¹ý (Add Custom Markers with the Google Maps JavaScript API)
³²¾çÁÖ°³¹ßÀÚ
¡¤2020. 8. 28. 08:09
¿ì¸®´Â À¥ °³¹ßÀ» ÇÏ´Ù º¸¸é ±¸±Û¸Ê, ±¸±ÛÁöµµ(Google Map)À» ´Ù·ç´Â °æ¿ì°¡ Á¾Á¾ ÀÖ½À´Ï´Ù. ±×¶§¸¶´Ù ±¸±Û¸Ê¿¡ ¿ì¸®°¡ ¿øÇÏ´Â À§Ä¡¸¦ Ç¥ÇöÇϱâ À§Çؼ ¸¶Ä¿¸¦ »ç¿ëÇÏ°ï ÇÏÁÒ. ±âº»ÀûÀÎ ¸¶Ä¿ ±¸ÇöÀ¸·Î ³¡³ª´Â °æ¿ìµµ ÀÖÁö¸¸ ±¸ÇöÇÏ´Â ¼ºñ½º Ư»ö¿¡ ¸Â°Ô Ä¿½ºÅÒ ¸¶Ä¿¸¦ Àû¿ëÇØ¾ß µÇ´Â °æ¿ì°¡ ÀÖ½À´Ï´Ù. ÇÏÁö¸¸ ±¸±Û¸Ê¿¡¼ Á¦°øÇÏ°í ÀÖ´Â APIÀÇ ¸¶Ä¿(Marker)¸¦ È°¿ëÇؼ ¿ì¸®ÀÇ ¼ºñ½º¿¡ ¸Â´Â Ä¿½ºÅÒ ¸¶Ä¿¸¦ ±¸ÇöÇϱ⿡´Â ±²ÀåÈ÷ ¾î·Á¿òÀÌ ¸¹½À´Ï´Ù. (Ä¿½ºÅ͸¶ÀÌ¡ Çϱ⠳ʹ« ±î´Ù·Ó½À´Ï´Ù.)
À̹ø Æ÷½ºÆÿ¡¼´Â Google Map API¿¡¼ Á¦°øÇÏ´Â OverlayView Ŭ·¡½º¸¦ È°¿ëÇؼ Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker)¸¦ ±¸ÇöÇغ¸µµ·Ï ÇÏ°Ú½À´Ï´Ù.
µé¾î°¡±â Àü¿¡
Àú´Â À̹ø Æ÷½ºÆÿ¡¼ Vue °³¹ßȯ°æ¿¡¼ ±¸±Û¸Ê Ä¿½ºÅÒ ¸¶Ä¿ ¿¹½Ã¸¦ ±¸ÇöÇÒ ¿¹Á¤ÀÔ´Ï´Ù. ¹°·Ð ±âº» µ¿ÀÛ ¹æ½ÄÀº °°À¸¹Ç·Î React¸¦ È°¿ëÇϼŵµ µÇ°í, Pure Javascript·Î ±¸ÇöÇϼŵµ µË´Ï´Ù. ±¸ÇöÇϱâ ÆíÇϽŠ¶óÀ̺귯¸®·Î °³¹ßȯ°æÀ» ±¸¼ºÇØÁÖ¼¼¿ä!
¿¹½Ã¿¡ »ç¿ëµÉ »ùÇõ¥ÀÌÅÍ´Â ¾Æ·¡¿Í °°½À´Ï´Ù.
»ùÇõ¥ÀÌÅÍ
const sample = [
{ latitude: 37.5047592, longitude: 127.0415586, price: "₩52" },
{ latitude: 37.5082055, longitude: 127.0363408, price: "₩66" },
{ latitude: 37.5055726, longitude: 127.0294372, price: "₩40" },
{ latitude: 37.4994, longitude: 127.03545, price: "₩39" },
{ latitude: 37.4916279, longitude: 127.0289673, price: "₩43" },
{ latitude: 37.49479, longitude: 127.03665, price: "₩54" },
{ latitude: 37.5052889, longitude: 127.0258825, price: "₩50" },
{ latitude: 37.503028, longitude: 127.0237718, price: "₩47" },
{ latitude: 37.506151, longitude: 127.028389, price: "₩50" },
{ latitude: 37.505394, longitude: 127.028807, price: "₩16" },
{ latitude: 37.4918215, longitude: 127.0299, price: "₩38" },
{ latitude: 37.504824, longitude: 127.028217, price: "₩195" },
{ latitude: 37.5012203, longitude: 127.035459, price: "₩55" },
{ latitude: 37.49869, longitude: 127.0323734, price: "₩51" },
{ latitude: 37.5028748, longitude: 127.0394336, price: "₩32" },
{ latitude: 37.5065218, longitude: 127.0303014, price: "₩41" },
{ latitude: 37.4935486, longitude: 127.0280787, price: "₩49" },
{ latitude: 37.4995308, longitude: 127.0354614, price: "₩28" },
{ latitude: 37.50664, longitude: 127.03158, price: "₩64" },
{ latitude: 37.5024767, longitude: 127.0399139, price: "₩49" },
{ latitude: 37.5013577, longitude: 127.0357776, price: "₩40" },
{ latitude: 37.5024315, longitude: 127.0387326, price: "₩36" },
{ latitude: 37.500582, longitude: 127.041064, price: "₩151" },
{ latitude: 37.506508, longitude: 127.03227, price: "₩56" },
{ latitude: 37.505964, longitude: 127.031195, price: "₩64" },
{ latitude: 37.5059947, longitude: 127.0296956, price: "₩50" },
{ latitude: 37.502935, longitude: 127.039946, price: "₩37" },
{ latitude: 37.50271, longitude: 127.040521, price: "₩37" },
{ latitude: 37.50161, longitude: 127.04103, price: "₩43" },
{ latitude: 37.49901, longitude: 127.02851, price: "₩96" },
{ latitude: 37.497393, longitude: 127.029029, price: "₩42" },
{ latitude: 37.505412, longitude: 127.025293, price: "₩28" },
{ latitude: 37.5008366, longitude: 127.0389705, price: "₩41" },
{ latitude: 37.503903, longitude: 127.0350934, price: "₩57" },
{ latitude: 37.4988, longitude: 127.034, price: "₩42" },
{ latitude: 37.50406, longitude: 127.0273, price: "₩17" },
{ latitude: 37.495657, longitude: 127.0351384, price: "₩15" },
{ latitude: 37.5012302, longitude: 127.0422585, price: "₩42" },
{ latitude: 37.494725, longitude: 127.035201, price: "₩14" },
{ latitude: 37.500849, longitude: 127.039129, price: "₩62" },
{ latitude: 37.49232, longitude: 127.031682, price: "₩14" },
{ latitude: 37.502704, longitude: 127.039724, price: "₩42" },
{ latitude: 37.500988, longitude: 127.039632, price: "₩34" },
{ latitude: 37.496069, longitude: 127.02963, price: "₩33" },
{ latitude: 37.4958567, longitude: 127.0299851, price: "₩42" },
{ latitude: 37.499953, longitude: 127.031842, price: "₩37" },
{ latitude: 37.501198, longitude: 127.040513, price: "₩37" },
{ latitude: 37.50329, longitude: 127.03675, price: "₩24" },
{ latitude: 37.5000614, longitude: 127.0247841, price: "₩22" },
{ latitude: 37.50271, longitude: 127.03991, price: "₩33" },
];
±¸ÇöÇÒ °á°ú¹°
À̹ø Æ÷½ºÆÿ¡¼ ±¸ÇöÇÒ °á°ú¹°Àº ¿¡¾îºñ¾Øºñ(airbnb)¿¡¼ »ç¿ëÇÏ°í ÀÖ´Â ¾Æ·¡¿Í °°Àº ±¸±Û¸Ê UI¸¦ ±¸ÇöÇغ¼ °ÍÀÔ´Ï´Ù. (Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker) ºÎºÐ¸¸)
±¸ÇöÇϱâ
±¸ÇöÇϱâ Àü¿¡ Vue¿¡¼ ±¸±Û¸ÊÀ» ½±°Ô »ç¿ëÇϱâ À§ÇØ vue2-google-maps ¶óÀ̺귯¸®¸¦ È°¿ëÇؼ ±¸±Û¸Ê ÄÄÆ÷³ÍÆ®¸¦ ¼¼ÆÃÇÏ°Ú½À´Ï´Ù. ¸®¾×Æ®¿¡¼´Â ¾Æ·¡¿Í °°Àº ¶óÀ̺귯¸®¸¦ È°¿ëÇؼ °³¹ßÇÏ½Ã¸é µÇ°Ú³×¿ä.
ÆÐÅ°Áö ¼³Ä¡
±¸±Û¸Ê API¸¦ ½±°Ô »ç¿ëÇϱâ À§ÇØ vue2-google-maps ¶óÀ̺귯¸®¸¦ ¼³Ä¡ÇÕ´Ï´Ù.
npm install vue2-google-maps
// or
yarn add vue2-google-maps
¸®¾×Æ®¿¡¼´Â ¾Æ·¡¿Í °°Àº ¶óÀ̺귯¸®¸¦ È°¿ëÇؼ °³¹ßÇÏ½Ã¸é µÇ°Ú³×¿ä.
±¸±Û¸Ê ±âº» ¼³Á¤
±¸±Û¸ÊÀ» »ç¿ëÇϱâ À§Çؼ´Â ±âº»ÀûÀ¸·Î ±¸±Û API Key¸¦ ¹ß±Þ¹Þ°í ¾Æ·¡¿Í °°ÀÌ ¼¼ÆÃÇØ¾ß ÇÕ´Ï´Ù. ±¸±Û Áöµµ API Å°¸¦ ¹ß±Þ¹Þ°í ¾Æ·¡ key¿¡ ³Ö¾îÁÖ¼¼¿ä.
// main.js
import Vue from "vue";
import * as VueGoogleMaps from "vue2-google-maps";
Vue.use(VueGoogleMaps, {
load: {
key: "google ¸Ê API Å°¸¦ ³Ö¾îÁÖ¼¼¿ä.",
libraries: "places",
},
});
±¸±Û¸Ê ±âº» ¼³Á¤À» ¿Ï·áÇß´Ù¸é ÄÄÆ÷³ÍÆ®¿¡ ±¸±Û¸Ê ÄÄÆ÷³ÍÆ®¸¦ Ãß°¡ÇÕ´Ï´Ù. ±¸±Û¸Ê ÄÄÆ÷³ÍÆ®°¡ Á¤»óÀûÀ¸·Î ·»´õ¸µ µÇ´ÂÁö È®ÀÎÇϱâ À§ÇØ ÀÓÀÇÀÇ ÁÂÇ¥°ªÀ¸·Î center °ªÀ» ¼³Á¤ÇÕ´Ï´Ù. ±¸±Û¸ÊÀÇ »çÀÌÁ ½ºÅ©¸° »çÀÌÁî Àüü·Î ±¸¼ºÇϱâ À§ÇØ ÄÄÆ÷³ÍÆ® ½ºÅ¸ÀÏ °ªÀ» width 100vw, height 100vh·Î ±¸¼ºÇÕ´Ï´Ù.
// App.vue
<template>
<div id="app">
<GmapMap
ref="mapRef"
:center="center"
:zoom="16"
style="width: 100vw; height: 100vh" // ±¸±ÛÁöµµ »çÀÌÁî 100%
>
</GmapMap>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
// Å×½ºÆ® ÁÂÇ¥ µ¥ÀÌÅÍ
center: {
lat: 37.500131499999995,
lng: 127.03242579999998,
},
};
}
};
</script>
<style>
body {
margin: 0;
padding: 0;
}
</style>
Å×½ºÆ® ÁÂÇ¥ µ¥ÀÌÅ͸¦ ±¸±Û¸Ê ÄÄÆ÷³ÍÆ®ÀÇ center ¼Ó¼º °ªÀ¸·Î ÇÒ´çÇÏ¸é ¾Æ·¡¿Í °°ÀÌ ±¸±Û¸ÊÀÌ ·»´õ¸µ µÇ´Â °ÍÀ» È®ÀÎÇÒ ¼ö ÀÖ½À´Ï´Ù.
¸¶Ä¿ »ý¼ºÇϱâ
±¸±Û¸Ê¿¡ ƯÁ¤ ÁöÁ¡¿¡ À§Ä¡¸¦ Âï±â À§Çؼ´Â ¸¶Ä¿(Marker)¸¦ »ç¿ëÇØ¾ß ÇÕ´Ï´Ù. ¸¶Ä¿ÀÇ ±âº» µ¿ÀÛ ¿ø¸®´Â ¾Æ·¡ ±¸±Û¸Ê API ¹®¼¸¦ Âü°íÇϼ¼¿ä.
Àú´Â vue2-google-maps ¶óÀ̺귯¸®¿¡¼ Á¦°øÇÏ°í ÀÖ´Â GmapMarker ÄÄÆ÷³ÍÆ®¸¦ È°¿ëÇؼ ¸¶Ä¿¸¦ »ý¼ºÇÏ°Ú½À´Ï´Ù. ¸¶Ä¿ µ¥ÀÌÅ͸¦ Ãß°¡ÇÕ´Ï´Ù. ¸¶Ä¿ µ¥ÀÌÅÍ´Â À§°æµµ(lat, lng)·Î ±¸¼ºµÈ Æ÷Áö¼Ç(position) ÇÁ·ÎÆÛƼ·Î ±¸¼ºµÇ¾î ÀÖ½À´Ï´Ù.
<template>
<div id="app">
<GmapMap
ref="mapRef"
:center="center"
:zoom="16"
style="width: 100vw; height: 100vh"
>
<GmapMarker
:key="index"
v-for="(m, index) in markers"
:position="m.position"
/>
</GmapMap>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
center: {
lat: 37.500131499999995,
lng: 127.03242579999998,
},
// ¸¶Ä¿ µ¥ÀÌÅÍ Ãß°¡
markers: [
{
position: {
lat: 37.500131499999995,
lng: 127.03242579999998,
},
},
],
};
},
};
</script>
ÁÂÃøÀº À§ ÄÚµåÀÇ °á°ú¹°ÀÌ°í, ¿ìÃøÀº »ùÇõ¥ÀÌÅ͸¦ ¸¶Ä¿ µ¥ÀÌÅÍ Çü½Ä¿¡ ¸ÂÃç¼ ¸¶Ä¿¸¦ »ý¼ºÇÑ °á°ú¹°ÀÔ´Ï´Ù. »ùÇõ¥ÀÌÅÍ´Â µé¾î°¡±â Àü¿¡ Á¦°øÇص帰 µ¥ÀÌÅÍÀÔ´Ï´Ù.
Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker) ±¸Çö
Ä¿½ºÅÒ ¸¶Ä¿´Â ±¸±Û¸Ê API¿¡¼ Á¦°øÇÏ°í ÀÖ´Â ±â´ÉÀÔ´Ï´Ù. ÇÏÁö¸¸, Á¦°¡ Á¶±Ý ã¾Æº» °á°ú(Á¦°¡ ¸ø ã¾ÒÀ» ¼öµµ ÀÖ½À´Ï´Ù) ±¸±Û¸Ê API¿¡¼ Á¦°øÇÏ°í ÀÖ´Â Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker)´Â ±¸±Û¸Ê¿¡¼ Á¦°øÇÏ°í ÀÖ´Â ±âº» ¸¶Ä¿ ¾ÆÀÌÄÜÀ» º¯°æÇØÁÖ´Â ¼öÁØÀ¸·Î Á¦°øÇÏ°í ÀÖ½À´Ï´Ù. Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker)ÀÇ ±âº» µ¿ÀÛ ¿ø¸®´Â ¾Æ·¡ÀÇ ±¸±Û API ¹®¼¸¦ Âü°íÇϼ¼¿ä.
¿ì¸®´Â ÀÌÁ¦ ¿¡¾îºñ¾Øºñ¿¡¼ »ç¿ëÇÏ°í ÀÖ´Â ±¸±Û¸Ê Ä¿½ºÅÒ ¸¶Ä¿¿Í ¶È°°ÀÌ ¸¸µé¾îº¼ °ÍÀÔ´Ï´Ù.
¿ì¼± ±¸±Û¸Ê¿¡ ¸¶Ä¿µéÀ» ±¸¼ºÇϱâ À§ÇØ »ùÇõ¥ÀÌÅ͸¦ ÄÄÆ÷³ÍÆ®¿¡ Ãß°¡ÇØÁÖ¼¼¿ä. »ùÇõ¥ÀÌÅÍ´Â ¾Æ·¡¿Í °°½À´Ï´Ù.
// ¸¶Ä¿¸¦ ±¸ÇöÇϱâ À§ÇÑ »ùÇõ¥ÀÌÅÍ
const sample = [
{ latitude: 37.5047592, longitude: 127.0415586, price: "₩52" },
{ latitude: 37.5082055, longitude: 127.0363408, price: "₩66" },
{ latitude: 37.5055726, longitude: 127.0294372, price: "₩40" },
{ latitude: 37.4994, longitude: 127.03545, price: "₩39" },
{ latitude: 37.4916279, longitude: 127.0289673, price: "₩43" },
{ latitude: 37.49479, longitude: 127.03665, price: "₩54" },
{ latitude: 37.5052889, longitude: 127.0258825, price: "₩50" },
{ latitude: 37.503028, longitude: 127.0237718, price: "₩47" },
{ latitude: 37.506151, longitude: 127.028389, price: "₩50" },
{ latitude: 37.505394, longitude: 127.028807, price: "₩16" },
{ latitude: 37.4918215, longitude: 127.0299, price: "₩38" },
{ latitude: 37.504824, longitude: 127.028217, price: "₩195" },
{ latitude: 37.5012203, longitude: 127.035459, price: "₩55" },
{ latitude: 37.49869, longitude: 127.0323734, price: "₩51" },
{ latitude: 37.5028748, longitude: 127.0394336, price: "₩32" },
{ latitude: 37.5065218, longitude: 127.0303014, price: "₩41" },
{ latitude: 37.4935486, longitude: 127.0280787, price: "₩49" },
{ latitude: 37.4995308, longitude: 127.0354614, price: "₩28" },
{ latitude: 37.50664, longitude: 127.03158, price: "₩64" },
{ latitude: 37.5024767, longitude: 127.0399139, price: "₩49" },
{ latitude: 37.5013577, longitude: 127.0357776, price: "₩40" },
{ latitude: 37.5024315, longitude: 127.0387326, price: "₩36" },
{ latitude: 37.500582, longitude: 127.041064, price: "₩151" },
{ latitude: 37.506508, longitude: 127.03227, price: "₩56" },
{ latitude: 37.505964, longitude: 127.031195, price: "₩64" },
{ latitude: 37.5059947, longitude: 127.0296956, price: "₩50" },
{ latitude: 37.502935, longitude: 127.039946, price: "₩37" },
{ latitude: 37.50271, longitude: 127.040521, price: "₩37" },
{ latitude: 37.50161, longitude: 127.04103, price: "₩43" },
{ latitude: 37.49901, longitude: 127.02851, price: "₩96" },
{ latitude: 37.497393, longitude: 127.029029, price: "₩42" },
{ latitude: 37.505412, longitude: 127.025293, price: "₩28" },
{ latitude: 37.5008366, longitude: 127.0389705, price: "₩41" },
{ latitude: 37.503903, longitude: 127.0350934, price: "₩57" },
{ latitude: 37.4988, longitude: 127.034, price: "₩42" },
{ latitude: 37.50406, longitude: 127.0273, price: "₩17" },
{ latitude: 37.495657, longitude: 127.0351384, price: "₩15" },
{ latitude: 37.5012302, longitude: 127.0422585, price: "₩42" },
{ latitude: 37.494725, longitude: 127.035201, price: "₩14" },
{ latitude: 37.500849, longitude: 127.039129, price: "₩62" },
{ latitude: 37.49232, longitude: 127.031682, price: "₩14" },
{ latitude: 37.502704, longitude: 127.039724, price: "₩42" },
{ latitude: 37.500988, longitude: 127.039632, price: "₩34" },
{ latitude: 37.496069, longitude: 127.02963, price: "₩33" },
{ latitude: 37.4958567, longitude: 127.0299851, price: "₩42" },
{ latitude: 37.499953, longitude: 127.031842, price: "₩37" },
{ latitude: 37.501198, longitude: 127.040513, price: "₩37" },
{ latitude: 37.50329, longitude: 127.03675, price: "₩24" },
{ latitude: 37.5000614, longitude: 127.0247841, price: "₩22" },
{ latitude: 37.50271, longitude: 127.03991, price: "₩33" },
];
¹öÆ°À» Ŭ¸¯ÇßÀ» ¶§ ±¸±Û¸Ê ÄÄÆ÷³ÍÆ®¿¡ ¸¶Ä¿¸¦ Ãß°¡Çϱâ À§ÇØ ¹öÆ°À» Çϳª Ãß°¡ÇÕ´Ï´Ù. ±¸±Û¸Ê API¸¦ È°¿ëÇϱâ À§ÇØ ±¸±Û API °´Ã¼¸¦ °¡Á®¿É´Ï´Ù.
import { gmapApi as google } from "vue2-google-maps";
// like window.google
Àú´Â computed¿¡ google API °´Ã¼¸¦ Ãß°¡Çß½À´Ï´Ù. google API °´Ã¼°¡ È°¼ºÈµÇ¾úÀ» ¶§ Ä¿½ºÅÒ ¸¶Ä¿ Ŭ·¡½º¸¦ ¼¼ÆÃÇϱâ À§ÇØ watch¸¦ È°¿ëÇÕ´Ï´Ù. watch¿¡¼ google computed°ªÀÌ º¯°æµÊÀ» °¨ÁöÇϸé initCustomMarker ÇÔ¼ö¸¦ »ç¿ëÇؼ CustomMarker¿¡ Ä¿½ºÅÒ ¸¶Ä¿ Ŭ·¡½º¸¦ ÇÒ´çÇÕ´Ï´Ù.
<template>
<div id="app">
<button @click="addMarkers">add</button>
<button @click="clearMarkers">clear</button>
<GmapMap
ref="mapRef"
:center="center"
:zoom="16"
style="width: 100vw; height: 100vh"
/>
</div>
</template>
<script>
import shuffle from "lodash/shuffle";
import take from "lodash/take";
import initCustomMarker from "./custom-marker";
import { gmapApi as google } from "vue2-google-maps";
let CustomMarker;
const sample = [
{ latitude: 37.5047592, longitude: 127.0415586, price: "₩52" },
{ latitude: 37.5082055, longitude: 127.0363408, price: "₩66" },
{ latitude: 37.5055726, longitude: 127.0294372, price: "₩40" },
{ latitude: 37.4994, longitude: 127.03545, price: "₩39" },
{ latitude: 37.4916279, longitude: 127.0289673, price: "₩43" },
{ latitude: 37.49479, longitude: 127.03665, price: "₩54" },
{ latitude: 37.5052889, longitude: 127.0258825, price: "₩50" },
{ latitude: 37.503028, longitude: 127.0237718, price: "₩47" },
{ latitude: 37.506151, longitude: 127.028389, price: "₩50" },
{ latitude: 37.505394, longitude: 127.028807, price: "₩16" },
{ latitude: 37.4918215, longitude: 127.0299, price: "₩38" },
{ latitude: 37.504824, longitude: 127.028217, price: "₩195" },
{ latitude: 37.5012203, longitude: 127.035459, price: "₩55" },
{ latitude: 37.49869, longitude: 127.0323734, price: "₩51" },
{ latitude: 37.5028748, longitude: 127.0394336, price: "₩32" },
{ latitude: 37.5065218, longitude: 127.0303014, price: "₩41" },
{ latitude: 37.4935486, longitude: 127.0280787, price: "₩49" },
{ latitude: 37.4995308, longitude: 127.0354614, price: "₩28" },
{ latitude: 37.50664, longitude: 127.03158, price: "₩64" },
{ latitude: 37.5024767, longitude: 127.0399139, price: "₩49" },
{ latitude: 37.5013577, longitude: 127.0357776, price: "₩40" },
{ latitude: 37.5024315, longitude: 127.0387326, price: "₩36" },
{ latitude: 37.500582, longitude: 127.041064, price: "₩151" },
{ latitude: 37.506508, longitude: 127.03227, price: "₩56" },
{ latitude: 37.505964, longitude: 127.031195, price: "₩64" },
{ latitude: 37.5059947, longitude: 127.0296956, price: "₩50" },
{ latitude: 37.502935, longitude: 127.039946, price: "₩37" },
{ latitude: 37.50271, longitude: 127.040521, price: "₩37" },
{ latitude: 37.50161, longitude: 127.04103, price: "₩43" },
{ latitude: 37.49901, longitude: 127.02851, price: "₩96" },
{ latitude: 37.497393, longitude: 127.029029, price: "₩42" },
{ latitude: 37.505412, longitude: 127.025293, price: "₩28" },
{ latitude: 37.5008366, longitude: 127.0389705, price: "₩41" },
{ latitude: 37.503903, longitude: 127.0350934, price: "₩57" },
{ latitude: 37.4988, longitude: 127.034, price: "₩42" },
{ latitude: 37.50406, longitude: 127.0273, price: "₩17" },
{ latitude: 37.495657, longitude: 127.0351384, price: "₩15" },
{ latitude: 37.5012302, longitude: 127.0422585, price: "₩42" },
{ latitude: 37.494725, longitude: 127.035201, price: "₩14" },
{ latitude: 37.500849, longitude: 127.039129, price: "₩62" },
{ latitude: 37.49232, longitude: 127.031682, price: "₩14" },
{ latitude: 37.502704, longitude: 127.039724, price: "₩42" },
{ latitude: 37.500988, longitude: 127.039632, price: "₩34" },
{ latitude: 37.496069, longitude: 127.02963, price: "₩33" },
{ latitude: 37.4958567, longitude: 127.0299851, price: "₩42" },
{ latitude: 37.499953, longitude: 127.031842, price: "₩37" },
{ latitude: 37.501198, longitude: 127.040513, price: "₩37" },
{ latitude: 37.50329, longitude: 127.03675, price: "₩24" },
{ latitude: 37.5000614, longitude: 127.0247841, price: "₩22" },
{ latitude: 37.50271, longitude: 127.03991, price: "₩33" },
];
export default {
name: "App",
data() {
return {
CustomMarker: null,
center: {
lat: 37.500131499999995,
lng: 127.03242579999998,
},
data: sample,
markers: [],
};
},
computed: {
google,
},
watch: {
google(val) {
CustomMarker = initCustomMarker(val);
},
},
methods: {
clearMarkers() {
this.markers.map((marker) => {
marker.onRemove();
});
this.markers = [];
},
addMarkers() {
take(shuffle(this.data), 20).map(({ price, latitude, longitude }, i) => {
const el = document.createElement("div");
el.textContent = price;
el.setAttribute("data-marker-index", i);
const t = new CustomMarker(
new this.google.maps.LatLng(latitude, longitude),
el
);
this.$refs["mapRef"].$mapPromise.then((map) => {
// Áöµµ¿¡ Ä¿½ºÅÒ ¸¶Ä¿¸¦ ¼¼ÆÃÇÑ´Ù.
t.setMap(map);
});
// ¸¶Ä¿ ÀνºÅϽºµéÀ» ÀúÀåÇÑ´Ù.
this.markers.push(t);
});
},
},
};
</script>
initCustomMarker ÇÔ¼ö´Â ¾Æ·¡¿Í °°½À´Ï´Ù. ±¸±Û¸Ê APIÀÇ OverlayView Ŭ·¡½º¸¦ È°¿ëÇؼ Ä¿½ºÅÒ ¸¶Ä¿¸¦ ±¸ÇöÇÕ´Ï´Ù. ±¸±Û¸Ê API OverlayViewÀÇ ±âº» µ¿ÀÛ ¿ø¸®´Â ¾Æ·¡¸¦ Âü°íÇϼ¼¿ä.
±¸±Û¸Ê API °´Ã¼¸¦ È®ÀåÇؼ Ä¿½ºÅÒ¸¶Ä¿ Ŭ·¡½º¸¦ »ý¼ºÇØ¾ß µÇ±â ¶§¹®¿¡ ±¸±Û¸Ê API °´Ã¼¸¦ Àü´Þ¹Þ¾Æ¾ß ÇÕ´Ï´Ù.
// custom-marker.js
export default (google) => {
return class CustomMarker extends google.maps.OverlayView {
constructor(position, content, options = { classname: "custom-marker" }) {
super();
const { classname } = options;
this.options = options;
this.position = position;
content.classList.add(classname);
const bubbleAnchor = document.createElement("div");
bubbleAnchor.classList.add(`${classname}-anchor`);
bubbleAnchor.appendChild(content);
this.containerDiv = document.createElement("div");
this.containerDiv.classList.add(`${classname}-container`);
this.containerDiv.appendChild(bubbleAnchor);
}
onAdd() {
this.getPanes().floatPane.appendChild(this.containerDiv);
}
onRemove() {
if (this.containerDiv.parentElement) {
this.containerDiv.parentElement.removeChild(this.containerDiv);
}
}
draw() {
const divPosition = this.getProjection().fromLatLngToDivPixel(
this.position
);
const display =
Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
? "block"
: "none";
if (display === "block") {
this.containerDiv.style.left = divPosition.x + "px";
this.containerDiv.style.top = divPosition.y + "px";
}
if (this.containerDiv.style.display !== display) {
this.containerDiv.style.display = display;
}
}
};
};
¿ì¸®°¡ ¸¸µç Ä¿½ºÅÒ ¸¶Ä¿ÀÇ ½ºÅ¸ÀÏÀ» Ãß°¡ÇÕ´Ï´Ù. Ä¿½ºÅÒ ¸¶Ä¿ ½ºÅ¸ÀÏÀº ¿ì¸®°¡ Ä¿½ºÅÒ ¸¶Ä¿¸¦ Ãß°¡ÇÒ ÄÄÆ÷³ÍÆ®¿¡ Ãß°¡ÇÏ°Ú½À´Ï´Ù.
// ¸¶Ä¿ ½ºÅ¸ÀÏ
<style lang="scss">
//
.custom-marker {
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -100%);
background-color: white;
padding: 0 8px;
border-radius: 28px;
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px,
rgba(0, 0, 0, 0.18) 0px 1px 2px;
color: #222;
overflow-y: auto;
height: 28px;
line-height: 28px;
font-weight: bold;
cursor: pointer;
transition: transform 0.15s ease-in-out;
font-size: 14px;
}
.custom-marker:hover {
transform: translate(-50%, -100%) scale(1.2);
}
.custom-marker-anchor {
position: absolute;
width: 100%;
bottom: 8px;
left: 0;
}
.custom-marker-container {
cursor: auto;
height: 0;
position: absolute;
width: 200px;
&.active {
.custom-marker {
z-index: 1000;
background-color: #f7530e;
color: #fff;
transform: translate(-50%, -100%) scale(1.1);
}
}
}
</style>
¸¶Ä¿¸¦ Ãß°¡Çϱâ À§ÇØ addMarkers ¸Þ¼µå¸¦ Ãß°¡ÇÕ´Ï´Ù. lodash take, shuffle¸¦ ÀûÀýÇÏ°Ô È°¿ëÇؼ ·£´ýÀ¸·Î »ùÇõ¥ÀÌÅÍ¿¡¼ ÀÓÀÇÀÇ 20°³ÀÇ °ªÀ» °¡Á®¿Í¼ ȸ鿡 ¸¶Ä¿¸¦ Âï¾îÁÖ´Â ÄÚµåÀÔ´Ï´Ù.
{
data() {
return {
...,
data: sample, // »ùÇõ¥ÀÌÅÍ
markers: [], // ¸¶Ä¿ ÀνºÅϽºµéÀ» ÀúÀåÇϱâ À§ÇØ markers µ¥ÀÌÅÍ Ãß°¡
};
},
methods: {
// ¸¶Ä¿ Ãß°¡
addMarkers() {
take(shuffle(this.data), 20).map(({ price, latitude, longitude }, i) => {
const el = document.createElement("div");
el.textContent = price;
el.setAttribute("data-marker-index", i);
const t = new CustomMarker(
new this.google.maps.LatLng(latitude, longitude),
el
);
this.$refs["mapRef"].$mapPromise.then((map) => {
// Áöµµ¿¡ Ä¿½ºÅÒ ¸¶Ä¿¸¦ ¼¼ÆÃÇÑ´Ù.
t.setMap(map);
});
// ¸¶Ä¿ ÀνºÅϽºµéÀ» ÀúÀåÇÑ´Ù.
this.markers.push(t);
});
},
}
}
add¹öÆ°¿¡ addMarkers ¸Þ¼µå¸¦ ¿¬°áÇÏ°í add ¹öÆ°À» ´©¸£¸é ¾Æ·¡¿Í °°ÀÌ Ä¿½ºÅÒ ¸¶Ä¿°¡ ±¸±Û¸Ê¿¡ ÂïÈ÷°Ô µË´Ï´Ù.
Ä¿½ºÅÒ ¸¶Ä¿¸¦ ±¸±Û¸Ê¿¡¼ Á¦°ÅÇÏ´Â ·ÎÁ÷µµ ÇÊ¿äÇÕ´Ï´Ù. Ä¿½ºÅÒ ¸¶Ä¿¸¦ Á¦°ÅÇÏ´Â Äڵ带 ±¸ÇöÇÕ´Ï´Ù. clearMarkers ¸Þ¼µå¸¦ Ãß°¡ÇÕ´Ï´Ù. ÄÚµå´Â ±²ÀåÈ÷ ´Ü¼øÇÕ´Ï´Ù. ¿ì¸®°¡ ¸¶Ä¿¸¦ Ãß°¡ÇÒ ¶§ markers µ¥ÀÌÅÍ¿¡ ¸ðµç ¸¶Ä¿ ÀνºÅϽº¸¦ ÀúÀåÇÑ °Í ±â¾ïÇϽÃÁÒ? ÀúÀåµÈ ¸¶Ä¿ ÀνºÅϽº¸¦ ¼øȸÇÏ¸é¼ ¸¶Ä¿ ÀνºÅϽºÀÇ onRemove ¸Þ¼µå¸¦ ½ÇÇàÇØÁÖ¸é ³¡ÀÔ´Ï´Ù. markers µ¥ÀÌÅÍÀÇ ¼øȸ°¡ ³¡³ª¸é markers µ¥ÀÌÅ͸¦ ÃʱâÈÇÕ´Ï´Ù.
methods: {
clearMarkers() {
this.markers.map((marker) => {
marker.onRemove();
});
this.markers = [];
},
...
}
Àüü ÄÚµå
±¸±Û Áöµµ(Google Map) API Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker) ±¸Çö ·ÎÁ÷ÀÇ Àüü ÄÚµå´Â ¾Æ·¡¿Í °°½À´Ï´Ù. ¾Æ·¡ Äڵ带 Âü°íÇؼ Ä¿½ºÅÒ ¸¶Ä¿(Custom Marker)¸¦ ±¸ÇöÇÏ½Ç ¶§ Âü°íÇØÁÖ¼¼¿ä.
<template>
<div id="app">
<button @click="addMarkers">add</button>
<button @click="clearMarkers">clear</button>
<GmapMap
ref="mapRef"
:center="center"
:zoom="16"
style="width: 100vw; height: 100vh"
/>
</div>
</template>
<script>
import shuffle from "lodash/shuffle";
import take from "lodash/take";
import initCustomMarker from "./custom-marker";
import { gmapApi as google } from "vue2-google-maps";
let CustomMarker;
const sample = [
{ latitude: 37.5047592, longitude: 127.0415586, price: "₩52" },
{ latitude: 37.5082055, longitude: 127.0363408, price: "₩66" },
{ latitude: 37.5055726, longitude: 127.0294372, price: "₩40" },
{ latitude: 37.4994, longitude: 127.03545, price: "₩39" },
{ latitude: 37.4916279, longitude: 127.0289673, price: "₩43" },
{ latitude: 37.49479, longitude: 127.03665, price: "₩54" },
{ latitude: 37.5052889, longitude: 127.0258825, price: "₩50" },
{ latitude: 37.503028, longitude: 127.0237718, price: "₩47" },
{ latitude: 37.506151, longitude: 127.028389, price: "₩50" },
{ latitude: 37.505394, longitude: 127.028807, price: "₩16" },
{ latitude: 37.4918215, longitude: 127.0299, price: "₩38" },
{ latitude: 37.504824, longitude: 127.028217, price: "₩195" },
{ latitude: 37.5012203, longitude: 127.035459, price: "₩55" },
{ latitude: 37.49869, longitude: 127.0323734, price: "₩51" },
{ latitude: 37.5028748, longitude: 127.0394336, price: "₩32" },
{ latitude: 37.5065218, longitude: 127.0303014, price: "₩41" },
{ latitude: 37.4935486, longitude: 127.0280787, price: "₩49" },
{ latitude: 37.4995308, longitude: 127.0354614, price: "₩28" },
{ latitude: 37.50664, longitude: 127.03158, price: "₩64" },
{ latitude: 37.5024767, longitude: 127.0399139, price: "₩49" },
{ latitude: 37.5013577, longitude: 127.0357776, price: "₩40" },
{ latitude: 37.5024315, longitude: 127.0387326, price: "₩36" },
{ latitude: 37.500582, longitude: 127.041064, price: "₩151" },
{ latitude: 37.506508, longitude: 127.03227, price: "₩56" },
{ latitude: 37.505964, longitude: 127.031195, price: "₩64" },
{ latitude: 37.5059947, longitude: 127.0296956, price: "₩50" },
{ latitude: 37.502935, longitude: 127.039946, price: "₩37" },
{ latitude: 37.50271, longitude: 127.040521, price: "₩37" },
{ latitude: 37.50161, longitude: 127.04103, price: "₩43" },
{ latitude: 37.49901, longitude: 127.02851, price: "₩96" },
{ latitude: 37.497393, longitude: 127.029029, price: "₩42" },
{ latitude: 37.505412, longitude: 127.025293, price: "₩28" },
{ latitude: 37.5008366, longitude: 127.0389705, price: "₩41" },
{ latitude: 37.503903, longitude: 127.0350934, price: "₩57" },
{ latitude: 37.4988, longitude: 127.034, price: "₩42" },
{ latitude: 37.50406, longitude: 127.0273, price: "₩17" },
{ latitude: 37.495657, longitude: 127.0351384, price: "₩15" },
{ latitude: 37.5012302, longitude: 127.0422585, price: "₩42" },
{ latitude: 37.494725, longitude: 127.035201, price: "₩14" },
{ latitude: 37.500849, longitude: 127.039129, price: "₩62" },
{ latitude: 37.49232, longitude: 127.031682, price: "₩14" },
{ latitude: 37.502704, longitude: 127.039724, price: "₩42" },
{ latitude: 37.500988, longitude: 127.039632, price: "₩34" },
{ latitude: 37.496069, longitude: 127.02963, price: "₩33" },
{ latitude: 37.4958567, longitude: 127.0299851, price: "₩42" },
{ latitude: 37.499953, longitude: 127.031842, price: "₩37" },
{ latitude: 37.501198, longitude: 127.040513, price: "₩37" },
{ latitude: 37.50329, longitude: 127.03675, price: "₩24" },
{ latitude: 37.5000614, longitude: 127.0247841, price: "₩22" },
{ latitude: 37.50271, longitude: 127.03991, price: "₩33" },
];
export default {
name: "App",
data() {
return {
CustomMarker: null,
center: {
lat: 37.500131499999995,
lng: 127.03242579999998,
},
data: sample,
markers: [],
};
},
computed: {
google,
},
watch: {
google(val) {
CustomMarker = initCustomMarker(val);
},
},
methods: {
clearMarkers() {
this.markers.map((marker) => {
marker.onRemove();
});
this.markers = [];
},
addMarkers() {
take(shuffle(this.data), 20).map(({ price, latitude, longitude }, i) => {
const el = document.createElement("div");
el.textContent = price;
el.setAttribute("data-marker-index", i);
const t = new CustomMarker(
new this.google.maps.LatLng(latitude, longitude),
el
);
this.$refs["mapRef"].$mapPromise.then((map) => {
// Áöµµ¿¡ Ä¿½ºÅÒ ¸¶Ä¿¸¦ ¼¼ÆÃÇÑ´Ù.
t.setMap(map);
});
// ¸¶Ä¿ ÀνºÅϽºµéÀ» ÀúÀåÇÑ´Ù.
this.markers.push(t);
});
},
},
};
</script>
<style>
body {
margin: 0;
padding: 0;
}
</style>
<style lang="scss">
.custom-marker {
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -100%);
background-color: white;
padding: 0 8px;
border-radius: 28px;
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px,
rgba(0, 0, 0, 0.18) 0px 1px 2px;
color: #222;
overflow-y: auto;
height: 28px;
line-height: 28px;
font-weight: bold;
cursor: pointer;
transition: transform 0.15s ease-in-out;
font-size: 14px;
}
.custom-marker:hover {
transform: translate(-50%, -100%) scale(1.2);
}
.custom-marker-anchor {
position: absolute;
width: 100%;
bottom: 8px;
left: 0;
}
.custom-marker-container {
cursor: auto;
height: 0;
position: absolute;
width: 200px;
&.active {
.custom-marker {
z-index: 1000;
background-color: #f7530e;
color: #fff;
transform: translate(-50%, -100%) scale(1.1);
}
}
}
</style>
export default (google) => {
return class CustomMarker extends google.maps.OverlayView {
constructor(position, content, options = { classname: "custom-marker" }) {
super();
const { classname } = options;
this.options = options;
this.position = position;
content.classList.add(classname);
const bubbleAnchor = document.createElement("div");
bubbleAnchor.classList.add(`${classname}-anchor`);
bubbleAnchor.appendChild(content);
this.containerDiv = document.createElement("div");
this.containerDiv.classList.add(`${classname}-container`);
this.containerDiv.appendChild(bubbleAnchor);
}
onAdd() {
this.getPanes().floatPane.appendChild(this.containerDiv);
}
onRemove() {
if (this.containerDiv.parentElement) {
this.containerDiv.parentElement.removeChild(this.containerDiv);
}
}
draw() {
const divPosition = this.getProjection().fromLatLngToDivPixel(
this.position
);
// Hide the popup when it is far out of view.
const display =
Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
? "block"
: "none";
if (display === "block") {
this.containerDiv.style.left = divPosition.x + "px";
this.containerDiv.style.top = divPosition.y + "px";
}
if (this.containerDiv.style.display !== display) {
this.containerDiv.style.display = display;
}
}
};
};
Å×»þ¸£
2020.08.30 11:12 ½Å°í
Àߺ¸°í°©´Ï´Ù~
±¸±ÛÁöµµ
2020.11.09 15:59
vue·Î Çغôµ¥ npm run serve ½ÃÄѵµ ¿À·ù Àڲٶ߳׿ä..
±¸±ÛÁöµµ
2020.11.09 16:02
vuex, vue2-google-maps, sass-loader ´Ù ¼³Ä¡ Çغôµ¥ ¸ðµâ°ú ¹®¹ý ±¸Á¶ ¹®Á¦·Î ½ÇÇàÀÌ ¾ÈµÅ¿ä
±è¹ÎÁö
2021.01.04 10:03
¾È³çÇϼ¼¿ä! Æ÷½ºÆà Àß º¸¾Ò½À´Ï´Ù.
±Ã±ÝÇÑ °Ô Àִµ¥, Ȥ½Ã ¸¶Ä¿¸¦ 1,000°³ ÀÌ»óÀ¸·Î ¾ÆÁÖ ¸¹ÀÌ ²ÈÀ¸·Á Çصµ ÀÌ·¯ÇÑ ÄÚµù ¹æ¹ýÀ¸·Î ÇØ°áÇÒ ¼ö ÀÖÀ»±î¿ä?