Render smoother findmydevice history paths
This commit is contained in:
+36
-74
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user