findmydevice 재생 배속 자동 조정
This commit is contained in:
@@ -71,7 +71,7 @@ Naver Map에는 단일 Polyline 중심의 부드러운 경로선을 먼저 그
|
|||||||
|
|
||||||
집 판정은 설정된 집 좌표를 중심점으로 한 50m 반경을 기준으로 합니다. GPS 튐 보정이 켜져 있으면 집 주변 1.2km 안의 지점은 집 주변 GPS 흔들림으로 흡수하고, 외출은 연속된 외부 지점이 5분 이상 지속될 때만 세션으로 확정합니다. 외부 지점 사이의 공백이 2시간을 넘으면 해당 구간은 알 수 없는 구간으로 보고 외출 지속시간에 합산하지 않습니다. 여러 번 나갔다 돌아온 경우 집 체류 시간은 합산하고, 외출은 출발/복귀 단위의 세션으로 따로 보여줍니다.
|
집 판정은 설정된 집 좌표를 중심점으로 한 50m 반경을 기준으로 합니다. GPS 튐 보정이 켜져 있으면 집 주변 1.2km 안의 지점은 집 주변 GPS 흔들림으로 흡수하고, 외출은 연속된 외부 지점이 5분 이상 지속될 때만 세션으로 확정합니다. 외부 지점 사이의 공백이 2시간을 넘으면 해당 구간은 알 수 없는 구간으로 보고 외출 지속시간에 합산하지 않습니다. 여러 번 나갔다 돌아온 경우 집 체류 시간은 합산하고, 외출은 출발/복귀 단위의 세션으로 따로 보여줍니다.
|
||||||
|
|
||||||
기록 시간 슬라이더를 움직이면 선택 마커가 해당 시점으로 이동하고, 지도 아래에는 `월. 일. 시간` 형식의 선택 시각만 간결하게 표시합니다. 경로 재생도 같은 선택 마커를 사용합니다. 80m 반경 안에서 10분 이상 머문 구간은 체류 지점 마커로 묶어 표시하며, 클릭 시 체류 시간과 시작/종료 시각을 확인할 수 있습니다.
|
기록 시간 슬라이더를 움직이면 선택 마커가 해당 시점으로 이동하고, 지도 아래에는 `월. 일. 시간` 형식의 선택 시각만 간결하게 표시합니다. 경로 재생도 같은 선택 마커를 사용합니다. 재생 버튼은 지점 수에 따라 의미 있는 배속만 남겨 `2x`, `5x`, `10x`, `20x`, `50x`, `100x`, `500x` 순서로 돌고, 각 배속에서 최소 80단계 이상 보여줄 수 없으면 해당 배속은 건너뜁니다. 80m 반경 안에서 10분 이상 머문 구간은 체류 지점 마커로 묶어 표시하며, 클릭 시 체류 시간과 시작/종료 시각을 확인할 수 있습니다.
|
||||||
|
|
||||||
라이브 위치에는 설정된 집 50m 원, GPS 정확도 원, 500m/1km 현재 위치 반경 원을 함께 표시합니다. `Naver 지도` 버튼은 히스토리 선택 지점 또는 현재 위치를 Naver 지도에서 바로 엽니다.
|
라이브 위치에는 설정된 집 50m 원, GPS 정확도 원, 500m/1km 현재 위치 반경 원을 함께 표시합니다. `Naver 지도` 버튼은 히스토리 선택 지점 또는 현재 위치를 Naver 지도에서 바로 엽니다.
|
||||||
|
|
||||||
|
|||||||
+58
-14
@@ -439,6 +439,8 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
|
|||||||
selectedMarker: null,
|
selectedMarker: null,
|
||||||
playbackTimer: null,
|
playbackTimer: null,
|
||||||
playbackIndex: 0,
|
playbackIndex: 0,
|
||||||
|
playbackSpeed: 1,
|
||||||
|
playbackSpeedOptions: [1],
|
||||||
rangeCircles: [],
|
rangeCircles: [],
|
||||||
homeCircle: null,
|
homeCircle: null,
|
||||||
homeCenter: null,
|
homeCenter: null,
|
||||||
@@ -469,6 +471,10 @@ let langCode = localStorage.getItem("currentLang") || deviceLang,
|
|||||||
_overrideRange,
|
_overrideRange,
|
||||||
wakeLock = null;
|
wakeLock = null;
|
||||||
|
|
||||||
|
const PLAYBACK_SPEEDS = [2, 5, 10, 20, 50, 100, 500];
|
||||||
|
const PLAYBACK_MIN_VISIBLE_STEPS = 80;
|
||||||
|
const PLAYBACK_TICK_MS = 160;
|
||||||
|
|
||||||
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/';
|
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/';
|
||||||
const BACK_LINK_PATH_TITLE = {
|
const BACK_LINK_PATH_TITLE = {
|
||||||
'/': '채건닷컴',
|
'/': '채건닷컴',
|
||||||
@@ -1397,7 +1403,7 @@ function updateMapToolLabels() {
|
|||||||
const t = translations[langCode] || translations.ko;
|
const t = translations[langCode] || translations.ko;
|
||||||
const labelMap = {
|
const labelMap = {
|
||||||
fit: t.mapFit,
|
fit: t.mapFit,
|
||||||
play: mapState.playbackTimer ? t.mapPause : t.mapPlay,
|
play: mapState.playbackTimer ? `${t.mapPause} ${mapState.playbackSpeed}x` : t.mapPlay,
|
||||||
filter: t.historyFiltered,
|
filter: t.historyFiltered,
|
||||||
naver: t.openNaverMap,
|
naver: t.openNaverMap,
|
||||||
};
|
};
|
||||||
@@ -1904,18 +1910,26 @@ function stopHistoryPlayback() {
|
|||||||
updateMapToolLabels();
|
updateMapToolLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleHistoryPlayback() {
|
function getPlaybackSpeedOptions(points) {
|
||||||
if (!mapState.map || !mapState.historyPoints?.length) return;
|
const count = Array.isArray(points) ? points.length : 0;
|
||||||
if (mapState.playbackTimer) {
|
const speeds = PLAYBACK_SPEEDS.filter(speed => count / speed >= PLAYBACK_MIN_VISIBLE_STEPS);
|
||||||
stopHistoryPlayback();
|
return speeds.length ? speeds : [1];
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
function startHistoryPlayback(speed) {
|
||||||
|
const points = mapState.historyPoints;
|
||||||
|
if (!mapState.map || !points?.length) return;
|
||||||
|
|
||||||
|
stopHistoryPlayback();
|
||||||
|
mapState.playbackSpeed = speed;
|
||||||
|
|
||||||
|
if (mapState.playbackIndex >= points.length - 1) {
|
||||||
|
mapState.playbackIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const points = mapState.historyPoints;
|
|
||||||
let index = 0;
|
|
||||||
if (!mapState.playbackMarker) {
|
if (!mapState.playbackMarker) {
|
||||||
mapState.playbackMarker = new naver.maps.Marker({
|
mapState.playbackMarker = new naver.maps.Marker({
|
||||||
position: points[0].latlng,
|
position: points[mapState.playbackIndex].latlng,
|
||||||
map: mapState.map,
|
map: mapState.map,
|
||||||
zIndex: 120,
|
zIndex: 120,
|
||||||
icon: {
|
icon: {
|
||||||
@@ -1925,19 +1939,49 @@ function toggleHistoryPlayback() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mapState.playbackMarker.setMap(mapState.map);
|
mapState.playbackMarker.setMap(mapState.map);
|
||||||
mapState.playbackMarker.setPosition(points[0].latlng);
|
mapState.playbackMarker.setPosition(points[mapState.playbackIndex].latlng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tick = 0;
|
||||||
mapState.playbackTimer = setInterval(() => {
|
mapState.playbackTimer = setInterval(() => {
|
||||||
|
const index = Math.min(mapState.playbackIndex || 0, points.length - 1);
|
||||||
const point = points[index];
|
const point = points[index];
|
||||||
moveSelectedHistoryPoint(index, false);
|
moveSelectedHistoryPoint(index, false);
|
||||||
if (index % 8 === 0) mapState.map.panTo(point.latlng);
|
if (tick % 8 === 0) mapState.map.panTo(point.latlng);
|
||||||
index += 1;
|
|
||||||
if (index >= points.length) stopHistoryPlayback();
|
const nextIndex = index + speed;
|
||||||
}, Math.max(120, Math.min(450, 10000 / points.length)));
|
if (nextIndex >= points.length) {
|
||||||
|
moveSelectedHistoryPoint(points.length - 1, false);
|
||||||
|
stopHistoryPlayback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapState.playbackIndex = nextIndex;
|
||||||
|
tick += 1;
|
||||||
|
}, PLAYBACK_TICK_MS);
|
||||||
updateMapToolLabels();
|
updateMapToolLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleHistoryPlayback() {
|
||||||
|
if (!mapState.map || !mapState.historyPoints?.length) return;
|
||||||
|
const points = mapState.historyPoints;
|
||||||
|
const options = getPlaybackSpeedOptions(points);
|
||||||
|
mapState.playbackSpeedOptions = options;
|
||||||
|
|
||||||
|
if (mapState.playbackTimer) {
|
||||||
|
const currentIndex = options.indexOf(mapState.playbackSpeed);
|
||||||
|
const nextSpeed = currentIndex >= 0 ? options[currentIndex + 1] : options[0];
|
||||||
|
if (nextSpeed) {
|
||||||
|
startHistoryPlayback(nextSpeed);
|
||||||
|
} else {
|
||||||
|
stopHistoryPlayback();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startHistoryPlayback(options[0]);
|
||||||
|
}
|
||||||
|
|
||||||
function clearTrackLines() {
|
function clearTrackLines() {
|
||||||
if (mapState.trackLine) {
|
if (mapState.trackLine) {
|
||||||
mapState.trackLine.setMap(null);
|
mapState.trackLine.setMap(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user