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; }
์นด์นด์ค ๋๋ฒจ๋กํผ ์ค์
๋ ์ด๋ฏธ ์ฑ์ด ๋ฑ๋ก๋์ด์์๊ธฐ ๋๋ฌธ์ ์๋์ ์์ ๋ง ํ๋ฉด ๋์๋ค.
1) ์ฑ > ์ผ๋ฐ > ์ฑ ํค > javascriptํค๋ฅผ apiํค๋ก ์ฌ์ฉ
2) ์นด์นด์ค๋งต > ์ฌ์ฉ์ค์ on


728x90
๋ฐ์ํ
LIST