From b34cd5e17f3c49073b8216a421850f8aa88b71d4 Mon Sep 17 00:00:00 2001 From: seo Date: Sat, 11 Jul 2026 07:24:30 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EC=9E=AC=EC=83=9D=20?= =?UTF-8?q?=EC=A4=91=20=EC=9E=90=EB=8F=99=20=EA=B0=B1=EC=8B=A0=20=EB=A6=AC?= =?UTF-8?q?=EC=85=8B=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ js/main.js | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 182fcd7..04c13e1 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ Naver Map에는 단일 Polyline 중심의 부드러운 경로선을 먼저 그 기록 시간 슬라이더를 움직이면 얼굴 아이콘 선택 마커가 해당 시점으로 이동하고, 지도 아래에는 `월. 일. 시간` 형식의 선택 시각만 간결하게 표시합니다. 경로 재생도 같은 얼굴 아이콘 마커를 사용합니다. 재생 버튼은 지점 수에 따라 의미 있는 배속만 남겨 `2x`, `5x`, `10x`, `20x`, `50x`, `100x`, `500x` 순서로 돌고, 각 배속에서 최소 80단계 이상 보여줄 수 없으면 해당 배속은 건너뜁니다. 80m 반경 안에서 10분 이상 머문 구간은 체류 지점 마커로 묶어 표시하며, 클릭 시 체류 시간과 시작/종료 시각을 확인할 수 있습니다. +재생 중 주기적 위치 갱신으로 히스토리 경로가 다시 그려져도 현재 재생 위치를 유지합니다. 재생 타이머는 시작 당시 지점 목록에 고정하지 않고 최신 히스토리 지점 목록을 기준으로 계속 진행합니다. + 라이브 위치에는 설정된 집 150m 원과 GPS 정확도 원만 표시합니다. 히스토리 범위 조회나 기록 재생 중에는 현재 위치 사진 마커와 정확도 원을 숨기고, 종료 지점 정보창을 자동으로 열지 않습니다. `Naver 지도` 버튼은 히스토리 선택 지점 또는 현재 위치를 Naver 지도에서 바로 엽니다. ## 센서 표시 정책 diff --git a/js/main.js b/js/main.js index c1f3bc9..4164163 100644 --- a/js/main.js +++ b/js/main.js @@ -1964,14 +1964,20 @@ function startHistoryPlayback(speed) { let tick = 0; mapState.playbackTimer = setInterval(() => { - const index = Math.min(mapState.playbackIndex || 0, points.length - 1); - const point = points[index]; + const currentPoints = mapState.historyPoints || []; + if (!currentPoints.length) { + stopHistoryPlayback(); + return; + } + + const index = Math.min(mapState.playbackIndex || 0, currentPoints.length - 1); + const point = currentPoints[index]; moveSelectedHistoryPoint(index, false); if (tick % 8 === 0) mapState.map.panTo(point.latlng); const nextIndex = index + speed; - if (nextIndex >= points.length) { - moveSelectedHistoryPoint(points.length - 1, false); + if (nextIndex >= currentPoints.length) { + moveSelectedHistoryPoint(currentPoints.length - 1, false); stopHistoryPlayback(); return; } @@ -2272,9 +2278,13 @@ function applyHistoryCounts(data) { function renderHistoryPoints(rawPoints) { if (!mapState.map || !rawPoints?.length) return; + const keepPlaybackIndex = !!mapState.playbackTimer; + const previousPlaybackIndex = Number.isFinite(mapState.playbackIndex) ? mapState.playbackIndex : 0; const points = filterHistoryOutliers(rawPoints); mapState.historyPoints = points; - mapState.playbackIndex = 0; + mapState.playbackIndex = keepPlaybackIndex + ? Math.max(0, Math.min(points.length - 1, previousPlaybackIndex)) + : 0; drawSpeedTrack(points); mapState.stayPoints = buildStayPoints(points); mapState.homeStats = buildHomeStats(points); @@ -2360,7 +2370,7 @@ function renderHistoryPoints(rawPoints) { mapState.historyBoundsFitted = true; } - if (!_overrideRange && !mapState.initialInfoOpened && mapState.historyEndMarker?.infoWindow) { + if (!_overrideRange && !keepPlaybackIndex && !mapState.initialInfoOpened && mapState.historyEndMarker?.infoWindow) { mapState.historyEndMarker.infoWindow.open(mapState.map, mapState.historyEndMarker); mapState.openInfoWindow = mapState.historyEndMarker.infoWindow; mapState.initialInfoOpened = true;