Recording map: follow toggle to free-pan while riding

New ⊕ button on the recording screen toggles map auto-follow. When off,
the map stays put so you can look around when unsure where you are (off
the track, or the track leads where there is no road); new GPS points no
longer recentre it. Tapping it back on recentres on the current position
and resumes following. Default on per screen entry (not persisted).

Guards followMode around panTo/setView in onPoint/onBatchUpdate/
onStateChange. Independent of the compass freeze/rotate controls.
This commit is contained in:
4aiman 2026-09-26 23:44:18 +03:00
commit d6fe47a924
6 changed files with 151 additions and 5 deletions

View file

@ -1246,6 +1246,7 @@ select {
.rec-mode-btn { right: 8px; bottom: 162px; font-size: 11px; }
.rec-route-toggle { right: 8px; bottom: 200px; }
.rec-dark-toggle { right: 8px; bottom: 238px; }
.rec-follow-btn { right: 8px; bottom: 276px; font-size: 18px; }
/* Dark map: invert ONLY the tile layer (overlays — track, route, roads, puck — keep
their colours). User-tuned filter on OSM standard tiles. Applies to every Leaflet map. */
body.dark-map .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) brightness(1.5); }

View file

@ -4469,6 +4469,7 @@ async function renderRecordingInline(container) {
// BOTH directions (orange = compass, green = GPS course) for visual comparison.
let rotOffset = 0; // 0/90/180/270 — whole-screen rotation
let compassFrozen = false; // X button: freeze current heading
let followMode = true; // ⊕ button: keep the map centred on the current position
let frozenHeading = 0; // heading captured at the moment of freezing
let smoothedHeading = null; // EMA of calibrated compass heading (deg, 0=N, CW)
let courseHeading = null; // EMA of GPS course over ground (deg, 0=N, CW)
@ -4634,6 +4635,26 @@ async function renderRecordingInline(container) {
};
btnWrap.appendChild(freezeBtn);
// Follow button — ⊕: toggle whether the map keeps re-centring on the current position.
// Off = free pan (look around when lost / off-route); tapping it back on recentres now.
let followBtn = document.createElement("button");
followBtn.className = "rec-map-btn rec-follow-btn active";
followBtn.textContent = "⊕"; // ⊕
followBtn.title = "Следить за позицией";
followBtn.onclick = () => {
followMode = !followMode;
followBtn.classList.toggle("active", followMode);
if (followMode) {
// Re-enabling: jump straight back to where we are now.
let pts = GPS.recording.points;
if (pts && pts.length) {
let last = pts.slice().sort((a, b) => new Date(a.ts) - new Date(b.ts))[pts.length - 1];
if (last) map.panTo([last.lat, last.lon]);
}
}
};
btnWrap.appendChild(followBtn);
// Roads toggle button
let roadsBtn = document.createElement("button");
roadsBtn.className = "rec-map-btn rec-roads-toggle";
@ -4811,7 +4832,7 @@ async function renderRecordingInline(container) {
if (ph) ph.remove();
statusEl.textContent = "GPS OK. Начните движение...";
statusEl.style.color = "#03dac6";
map.setView([p.lat, p.lon], 16);
if (followMode) map.setView([p.lat, p.lon], 16);
} else if (state === "recording") {
statusEl.textContent = "";
statusEl.style.color = "";
@ -4837,7 +4858,7 @@ async function renderRecordingInline(container) {
let ll = [p.lat, p.lon];
borderLine.addLatLng(ll);
trailLine.addLatLng(ll);
map.panTo(ll);
if (followMode) map.panTo(ll);
// Save position for next session start (avoid black screen)
DB.setProfileValue("last_lat", String(p.lat));
DB.setProfileValue("last_lon", String(p.lon));
@ -4854,7 +4875,7 @@ async function renderRecordingInline(container) {
if (recState === "waiting_gps") {
statusEl.textContent = "Поиск GPS...";
statusEl.style.color = "#ffb74d";
map.setView([p.lat, p.lon], 15);
if (followMode) map.setView([p.lat, p.lon], 15);
return;
}
if (recState === "ready") {
@ -5002,7 +5023,7 @@ async function renderRecordingInline(container) {
trailLine.setLatLngs(coords);
if (coords.length) {
let last = coords[coords.length - 1];
map.panTo(last);
if (followMode) map.panTo(last);
updateCourse(sorted[sorted.length - 1]); // course from the last point object (has bearing)
updateMe(last);
}