๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ‘ฉ‍๐Ÿ’ป

์นด์นด์˜ค๋งต api๋ฅผ ์ด์šฉํ•ด์„œ ์›น์— ํŠน์ • ์‹œ๊ฐ„๋ณ„๋กœ ์œ„์น˜ ๊ทธ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•

by chuyj15 2025. 8. 11.
728x90
๋ฐ˜์‘ํ˜•
SMALL

A์žฅ๋น„, B์žฅ๋น„์˜ n์ดˆ ์ดํ›„ ์œ„์น˜๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด ์žˆ์„ ๋•Œ ์›น์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๊ณ  ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋กœ ์Šค๋ฌด์Šคํ•˜๊ณ  ์ด๋™๋˜๋Š” ๋А๋‚Œ์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

const dummyTracks = {
A: [
{ lat: 37.5665, lng: 126.978 },
{ lat: 37.5667, lng: 126.9795 },
{ lat: 37.5669, lng: 126.981 },
{ lat: 37.5671, lng: 126.9822 },
{ lat: 37.5674, lng: 126.9838 },
],
B: [
{ lat: 37.565, lng: 126.976 },
{ lat: 37.5653, lng: 126.9776 },
{ lat: 37.5656, lng: 126.9791 },
{ lat: 37.566, lng: 126.9805 },
{ lat: 37.5664, lng: 126.982 },
],
};

 

 

 

* gps.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/web-default-layout}">
<head>
    <link rel="stylesheet" th:href="@{/css/gps/gps.css}">
    <script th:src="@{'//dapi.kakao.com/v2/maps/sdk.js?appkey=' + ${kakaoMapsApiKey}}"></script>
    <script type="module" th:src="@{/js/pages/gps/gps.js}"></script>
</head>
<body>
<div layout:fragment="content" id="main">
  <div class="wrapper">
    <div class="contents-header">
      <div class="page-title">GPS ๋ชจ๋‹ˆํ„ฐ๋ง</div>
    </div>
    <div class="contents-body">
      <div id="map"></div>
      <div class="legend">
        <div class="legend-item"><span class="dot a"></span>A ์žฅ๋น„</div>
        <div class="legend-item"><span class="dot b"></span>B ์žฅ๋น„</div>
      </div>
    </div>
  </div>
</div>
</body>
</html>

 

 

* gps.js 

let map;
let markers = {};
let tick = 0;
const trackIndex = { A: 0, B: 0 };

// ๋”๋ฏธ ๊ฒฝ๋กœ ๋ฐ์ดํ„ฐ: ์ดˆ(t)๋ณ„ ์ขŒํ‘œ ๋ชฉ๋ก
const dummyTracks = {
  A: [
    { lat: 37.5665, lng: 126.9780 },
    { lat: 37.5667, lng: 126.9795 },
    { lat: 37.5669, lng: 126.9810 },
    { lat: 37.5671, lng: 126.9822 },
    { lat: 37.5674, lng: 126.9838 },
  ],
  B: [
    { lat: 37.5650, lng: 126.9760 },
    { lat: 37.5653, lng: 126.9776 },
    { lat: 37.5656, lng: 126.9791 },
    { lat: 37.5660, lng: 126.9805 },
    { lat: 37.5664, lng: 126.9820 },
  ],
};

window.initMap = function() {
  const center = new kakao.maps.LatLng(37.5665, 126.9780);
  map = new kakao.maps.Map(document.getElementById('map'), {
    center,
    level: 4,
  });

  // ์ดˆ๊ธฐ ๋งˆ์ปค ์ƒ์„ฑ
  markers.A = new kakao.maps.Marker({
    position: new kakao.maps.LatLng(dummyTracks.A[0].lat, dummyTracks.A[0].lng),
  });
  markers.A.setMap(map);
  markers.B = new kakao.maps.Marker({
    position: new kakao.maps.LatLng(dummyTracks.B[0].lat, dummyTracks.B[0].lng),
  });
  markers.B.setMap(map);

  // ๋ถ€๋“œ๋Ÿฌ์šด ์ด๋™ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ (1์ดˆ์— ํ•œ ๊ตฌ๊ฐ„)
  animateMarker('A', markers.A, dummyTracks.A, 1000);
  animateMarker('B', markers.B, dummyTracks.B, 1000);
}

// Easing (๋ถ€๋“œ๋Ÿฌ์šด ๊ฐ€๊ฐ์†)
function easeInOutQuad(t) {
  return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}

// ๋‘ ์ขŒํ‘œ ์‚ฌ์ด๋ฅผ duration ๋™์•ˆ ๋ณด๊ฐ„ํ•˜๋ฉฐ ์ด๋™
function animateBetween(marker, from, to, durationMs, onDone) {
  const start = performance.now();
  function step(now) {
    let t = (now - start) / durationMs;
    if (t > 1) t = 1;
    const et = easeInOutQuad(t);
    const lat = from.lat + (to.lat - from.lat) * et;
    const lng = from.lng + (to.lng - from.lng) * et;
    marker.setPosition(new kakao.maps.LatLng(lat, lng));
    if (t < 1) {
      requestAnimationFrame(step);
    } else if (typeof onDone === 'function') {
      onDone();
    }
  }
  requestAnimationFrame(step);
}

function animateMarker(key, marker, track, durationMs) {
  const len = track.length;
  const i = trackIndex[key] % len;
  const from = track[i];
  const to = track[(i + 1) % len];
  animateBetween(marker, from, to, durationMs, () => {
    trackIndex[key] = (trackIndex[key] + 1) % len;
    animateMarker(key, marker, track, durationMs);
  });
}

// ์นด์นด์˜ค ์ง€๋„ ๋กœ๋“œ๊ฐ€ ๋๋‚œ ๋’ค ์ดˆ๊ธฐํ™”
window.addEventListener('load', () => {
  if (window.kakao && window.kakao.maps) {
    initMap();
  } else {
    const timer = setInterval(() => {
      if (window.kakao && window.kakao.maps) {
        clearInterval(timer);
        initMap();
      }
    }, 200);
  }
});

 

 

* gps.css

#main .wrapper {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

.contents-body {
  position: relative;
  flex: 1;
  display: flex;
  gap: 12px;
}

#map {
  flex: 1;
  min-height: 600px;
  border-radius: 8px;
  border: 1px solid var(--border-color);
}

.legend {
  width: 200px;
  background: #fff;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 12px;
  height: fit-content;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.legend-item .dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
}
.legend-item .dot.a { background: #ff4d4f; }
.legend-item .dot.b { background: #1677ff; }

 

 

์นด์นด์˜ค ๋””๋ฒจ๋กœํผ ์„ค์ •

https://developers.kakao.com/

 

๋‚œ ์ด๋ฏธ ์•ฑ์ด ๋“ฑ๋ก๋˜์–ด์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์˜ ์ž‘์—…๋งŒ ํ•˜๋ฉด ๋˜์—ˆ๋‹ค.

1) ์•ฑ > ์ผ๋ฐ˜ > ์•ฑ ํ‚ค > javascriptํ‚ค๋ฅผ apiํ‚ค๋กœ ์‚ฌ์šฉ

2) ์นด์นด์˜ค๋งต > ์‚ฌ์šฉ์„ค์ • on

 

 

 

 

 

 

 

728x90
๋ฐ˜์‘ํ˜•
LIST