fix: live-update GPS position marker and distance during tracking
All checks were successful
CI / update (push) Successful in 2m33s
All checks were successful
CI / update (push) Successful in 2m33s
- Make map variables reactive ($state) so effects fire when map initializes - Split single effect into polyline update + marker/view tracking - Marker now always follows latestPoint instead of staying at start position - Reset prevTrackLen on GPS restart to avoid skipping points - Hide marker until real GPS position arrives - Round saved distance to nearest 10m to avoid long floating-point values
This commit is contained in:
@@ -101,11 +101,11 @@
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
let liveMap = null;
|
||||
let liveMap = $state(null);
|
||||
/** @type {any} */
|
||||
let livePolyline = null;
|
||||
let livePolyline = $state(null);
|
||||
/** @type {any} */
|
||||
let liveMarker = null;
|
||||
let liveMarker = $state(null);
|
||||
/** @type {any} */
|
||||
let leafletLib = null;
|
||||
let prevTrackLen = 0;
|
||||
@@ -117,13 +117,13 @@
|
||||
destroy() {
|
||||
if (liveMap) {
|
||||
liveMap.remove();
|
||||
}
|
||||
liveMap = null;
|
||||
livePolyline = null;
|
||||
liveMarker = null;
|
||||
leafletLib = null;
|
||||
prevTrackLen = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,25 +140,28 @@
|
||||
}).addTo(liveMap);
|
||||
livePolyline = leafletLib.polyline([], { color: '#88c0d0', weight: 3 }).addTo(liveMap);
|
||||
liveMarker = leafletLib.circleMarker([0, 0], {
|
||||
radius: 6, fillColor: '#a3be8c', fillOpacity: 1, color: '#fff', weight: 2
|
||||
radius: 6, fillColor: '#a3be8c', fillOpacity: 1, color: '#fff', weight: 2, opacity: 0, fillOpacity: 0
|
||||
}).addTo(liveMap);
|
||||
|
||||
if (gps.track.length > 0) {
|
||||
// Restore existing trail on the polyline
|
||||
if (gpsStarted) {
|
||||
const pts = gps.track.map((/** @type {any} */ p) => [p.lat, p.lng]);
|
||||
livePolyline.setLatLngs(pts);
|
||||
liveMarker.setLatLng(pts[pts.length - 1]);
|
||||
liveMap.setView(pts[pts.length - 1], 16);
|
||||
}
|
||||
// Center on latest point — the marker $effect will also kick in
|
||||
const last = gps.track[gps.track.length - 1];
|
||||
liveMap.setView([last.lat, last.lng], 16);
|
||||
liveMarker.setLatLng([last.lat, last.lng]);
|
||||
prevTrackLen = gps.track.length;
|
||||
} else {
|
||||
// No track yet — show fallback until GPS kicks in
|
||||
liveMap.setView([51.5, 10], 16);
|
||||
// No track yet — get current position to center the map
|
||||
liveMap.setView([51.5, 10], 5);
|
||||
if ('geolocation' in navigator) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
if (liveMap) {
|
||||
const ll = [pos.coords.latitude, pos.coords.longitude];
|
||||
liveMap.setView(ll, 16);
|
||||
liveMarker.setLatLng(ll);
|
||||
liveMap.setView([pos.coords.latitude, pos.coords.longitude], 16);
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
@@ -204,25 +207,33 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Update polyline incrementally when new track points arrive
|
||||
$effect(() => {
|
||||
const len = gps.track.length;
|
||||
if (len > prevTrackLen && liveMap && gps.latestPoint) {
|
||||
if (gpsStarted) {
|
||||
// Only draw the trail once the workout has actually started
|
||||
if (len > prevTrackLen && liveMap && livePolyline && gpsStarted) {
|
||||
for (let i = prevTrackLen; i < len; i++) {
|
||||
const p = gps.track[i];
|
||||
livePolyline.addLatLng([p.lat, p.lng]);
|
||||
}
|
||||
}
|
||||
// Always update the position marker
|
||||
const pt = [gps.latestPoint.lat, gps.latestPoint.lng];
|
||||
liveMarker.setLatLng(pt);
|
||||
const zoom = liveMap.getZoom() || 16;
|
||||
liveMap.setView(pt, zoom);
|
||||
// Always sync prevTrackLen even if we didn't draw (e.g. pre-start)
|
||||
if (len > prevTrackLen) {
|
||||
prevTrackLen = len;
|
||||
}
|
||||
});
|
||||
|
||||
// Always keep marker and map view centered on the latest GPS position
|
||||
$effect(() => {
|
||||
const pt = gps.latestPoint;
|
||||
if (pt && liveMap && liveMarker) {
|
||||
const ll = [pt.lat, pt.lng];
|
||||
liveMarker.setLatLng(ll);
|
||||
liveMarker.setStyle({ opacity: 1, fillOpacity: 1 });
|
||||
const zoom = liveMap.getZoom() || 16;
|
||||
liveMap.setView(ll, zoom);
|
||||
}
|
||||
});
|
||||
|
||||
/** Check if any exercise in the workout is cardio */
|
||||
function hasCardioExercise() {
|
||||
return workout.exercises.some((/** @type {any} */ e) => {
|
||||
@@ -285,6 +296,7 @@
|
||||
// so the native service resets time/distance to zero
|
||||
await gps.stop();
|
||||
gps.reset();
|
||||
prevTrackLen = 0;
|
||||
}
|
||||
const started = await gps.start(getVoiceGuidanceConfig());
|
||||
if (started) {
|
||||
@@ -317,7 +329,7 @@
|
||||
if (wasGpsMode && gpsTrack.length >= 2) {
|
||||
// GPS workout: create a cardio exercise entry with the track attached,
|
||||
// just like a manually-added workout with GPX upload
|
||||
const filteredDistance = trackDistance(gpsTrack);
|
||||
const filteredDistance = Math.round(trackDistance(gpsTrack) * 100) / 100;
|
||||
const durationMin = (gpsTrack[gpsTrack.length - 1].timestamp - gpsTrack[0].timestamp) / 60000;
|
||||
const exerciseId = ACTIVITY_EXERCISE_MAP[actType ?? 'running'] ?? 'running';
|
||||
const exerciseName = getExerciseById(exerciseId)?.name ?? exerciseId;
|
||||
@@ -343,7 +355,7 @@
|
||||
// Manual workout: attach GPS to cardio exercises
|
||||
const workoutStart = new Date(sessionData.startTime).getTime();
|
||||
const filteredTrack = gpsTrack.filter((/** @type {any} */ p) => p.timestamp >= workoutStart);
|
||||
const filteredDistance = trackDistance(filteredTrack);
|
||||
const filteredDistance = Math.round(trackDistance(filteredTrack) * 100) / 100;
|
||||
|
||||
if (filteredTrack.length > 0) {
|
||||
for (const ex of sessionData.exercises) {
|
||||
|
||||
Reference in New Issue
Block a user