Render smoother findmydevice history paths

This commit is contained in:
seo
2026-06-17 19:42:59 +09:00
parent 8bbe43b8c4
commit 938868ea64
3 changed files with 173 additions and 79 deletions
+36 -74
View File
@@ -442,6 +442,8 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
homeCenter: null,
rawHistoryPoints: [],
historyPoints: [],
historyRawCount: null,
historyRenderCount: null,
historyStats: null,
stayPoints: [],
homeStats: null,
@@ -748,6 +750,7 @@ document.addEventListener('DOMContentLoaded', () => {
const data = await fetchDeviceData(true, shouldRequestHistory ? range : null);
updateDOMWithData(data);
if (!shouldUseHistory && data.history?.length > 0) {
applyHistoryCounts(data);
updateHistoryMarkers(data.history);
}
@@ -1529,6 +1532,8 @@ function buildHistoryStats(points) {
duration,
maxGap,
points: points.length,
rawPoints: mapState.historyRawCount || points.length,
renderPoints: mapState.historyRenderCount || points.length,
averageSpeed: duration > 0 ? distance / duration * 3.6 : 0,
stops: mapState.stayPoints?.length || 0,
home: mapState.homeStats,
@@ -1558,7 +1563,7 @@ function renderHistorySummary() {
<span>${t.historyDuration}</span><strong>${formatDuration(Math.round(stats.duration))}</strong>
<span>${t.historyAverageSpeed}</span><strong>${formatSpeed(stats.averageSpeed)}</strong>
<span>${t.historyMaxGap}</span><strong>${formatDuration(Math.round(stats.maxGap))}</strong>
<span>${t.historyPoints}</span><strong>${formatNumber(stats.points)}</strong>
<span>${t.historyPoints}</span><strong>${stats.rawPoints && stats.rawPoints !== stats.renderPoints ? `${formatNumber(stats.renderPoints)} / ${formatNumber(stats.rawPoints)}` : formatNumber(stats.points)}</strong>
<span>${t.historyStops}</span><strong>${formatNumber(stats.stops || 0)}</strong>
<span>${t.homeRadius}</span><strong>50m</strong>
<span>${t.homeStay}</span><strong>${home ? formatDuration(Math.round(home.homeSeconds)) : '-'}</strong>
@@ -1567,12 +1572,6 @@ function renderHistorySummary() {
<span>${t.lastReturn}</span><strong>${home?.lastReturn ? formatDateTime(home.lastReturn) : '-'}</strong>
<span>${t.currentPlaceStatus}</span><strong>${home?.status || '-'}</strong>
</div>
<div class="speed-legend">
${[0, 3, 6, 12, 35, 80, 130].map(kmh => {
const bucket = speedBucket(kmh);
return `<span><i style="--legend-color:${bucket.color}"></i>${bucket.label}</span>`;
}).join('')}
</div>
${recentSessions.length ? `
<div class="away-session-list">
<strong>${t.awaySessionTitle}</strong>
@@ -1688,31 +1687,25 @@ function drawSpeedTrack(points) {
clearTrackLines();
if (!points?.length || points.length < 2) return;
for (let i = 1; i < points.length; i++) {
const prev = points[i - 1];
const cur = points[i];
const seconds = Math.max(1, (toTimestamp(cur.time) - toTimestamp(prev.time)) / 1000);
const kmh = distanceMeters(prev, cur) / seconds * 3.6;
const line = new naver.maps.Polyline({
map: mapState.map,
path: [prev.latlng, cur.latlng],
strokeColor: speedColor(kmh),
strokeOpacity: 0.88,
strokeWeight: 4,
strokeLineCap: 'round',
strokeLineJoin: 'round',
endIcon: i === points.length - 1 ? naver.maps.PointingIcon?.BLOCK_ARROW : undefined,
endIconSize: 16,
clickable: true
});
line.segmentSpeed = kmh;
naver.maps.Event.addListener(line, 'click', () => {
const panel = document.getElementById('historySummaryPanel');
if (panel) panel.hidden = !panel.hidden;
});
mapState.trackLines.push(line);
}
mapState.trackLine = mapState.trackLines.at(-1) || null;
const path = points.map(point => point.latlng);
const line = new naver.maps.Polyline({
map: mapState.map,
path,
strokeColor: '#007aff',
strokeOpacity: 0.86,
strokeWeight: 5,
strokeLineCap: 'round',
strokeLineJoin: 'round',
endIcon: naver.maps.PointingIcon?.BLOCK_ARROW,
endIconSize: 16,
clickable: true
});
naver.maps.Event.addListener(line, 'click', () => {
const panel = document.getElementById('historySummaryPanel');
if (panel) panel.hidden = !panel.hidden;
});
mapState.trackLines.push(line);
mapState.trackLine = line;
}
function updateTimelinePanel() {
@@ -1903,6 +1896,8 @@ function resetHistoryLayer() {
}
mapState.rawHistoryPoints = [];
mapState.historyPoints = [];
mapState.historyRawCount = null;
mapState.historyRenderCount = null;
mapState.historyStats = null;
mapState.stayPoints = [];
mapState.homeStats = null;
@@ -1935,6 +1930,12 @@ function updateHistoryMarkers(history) {
renderHistoryPoints(points);
}
function applyHistoryCounts(data) {
const historyLength = Array.isArray(data?.history) ? data.history.length : 0;
mapState.historyRawCount = Number.isFinite(Number(data?.historyRawCount)) ? Number(data.historyRawCount) : historyLength;
mapState.historyRenderCount = Number.isFinite(Number(data?.historyRenderCount)) ? Number(data.historyRenderCount) : historyLength;
}
function renderHistoryPoints(rawPoints) {
if (!mapState.map || !rawPoints?.length) return;
const points = filterHistoryOutliers(rawPoints);
@@ -1997,13 +1998,6 @@ function renderHistoryPoints(rawPoints) {
mapState.historyStartMarker = makeMarker(first, 'start');
mapState.historyEndMarker = makeMarker(last, 'end');
const maxDots = 180;
const step = Math.max(1, Math.ceil(points.length / maxDots));
points.forEach((pos, idx) => {
if (idx === 0 || idx === points.length - 1 || idx % step !== 0) return;
mapState.smallMarkers.push(makeMarker(pos));
});
mapState.stayPoints.forEach(stay => {
const marker = new naver.maps.Marker({
position: stay.latlng,
@@ -2068,8 +2062,9 @@ function updateDOMWithData(data) {
// 히스토리 궤적/포인트 업데이트
if (data.history?.length > 0) {
updateHistoryMarkers(data.history);
}
applyHistoryCounts(data);
updateHistoryMarkers(data.history);
}
// 상대시간 갱신 중단
lastUpdateISOTime = null;
@@ -2252,39 +2247,6 @@ function hasValidCoords(lat, lng) {
typeof lng === 'number' && isFinite(lng);
}
function renderTrackFromDisplayedMarkers() {
if (!mapState.map) return;
// 시간 파싱 헬퍼(동일 로직)
const toTime = (v) => {
if (v instanceof Date) return v.getTime();
if (typeof v === 'number' && isFinite(v)) {
return v < 2e10 ? v * 1000 : v; // 초 → ms 보정
}
const t = Date.parse(v);
return isNaN(t) ? 0 : t;
};
// 현재 표시된 스몰마커만 정렬 → 경로 생성
const points = mapState.smallMarkers
.slice()
.sort((a, b) => toTime(a.userTime) - toTime(b.userTime))
.map(m => m.getPosition());
// 라인 갱신
if (!mapState.trackLine) {
mapState.trackLine = new naver.maps.Polyline({
map: mapState.map,
path: points,
strokeColor: '#007AFF',
strokeOpacity: 0.8,
strokeWeight: 3
});
} else {
mapState.trackLine.setPath(points);
}
}
function callDeviceWebhook(name, keepalive = false) {
return fetch('https://chaegeon.com/custom/findmydevice/php/api.php?action=webhook', {
method: 'POST',