From fe20a4d590fc48e2d30709ac75296e45990bbb1e Mon Sep 17 00:00:00 2001 From: 4aiman Date: Tue, 25 Aug 2026 20:48:27 +0300 Subject: [PATCH] Recording puck: point direction triangle UP (no jitter) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The direction triangle jumped around in GPS mode: rotation was set to the geo heading with rotateWithView adding map._bearing, and the two desynced (they should cancel in course-up but updated at different times). Since the map already rotates the active heading to the top, travel direction is always screen-up — so just point the triangle up (no rotateWithView, no per-frame rotation), shown whenever a heading is known. Compass mode still to be tested. --- PROJECT_MAP.md | 2 +- frontend/js/main.js | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PROJECT_MAP.md b/PROJECT_MAP.md index e90061b..c71b5a6 100644 --- a/PROJECT_MAP.md +++ b/PROJECT_MAP.md @@ -130,7 +130,7 @@ ### Экраны - **Сегодня** — баланс ккал, еда (продукты + блюда), GPS тренировки, активность, вес, сон, добавки, прогресс-бар жидкости, нутриенты (приход + запасы) - **Продукты** — подтабы [Продукты | Рецепты], поиск, карточки с КБЖУ, добавление в дневник -- **Трек** — GPS тренировки (запись с HR), карта с компасом (leaflet-rotate). Навигационный «пак» позиции (SVG divIcon, `rotateWithView`): круг цвета трека (на 20% темнее) с белой + 1px чёрной обводкой + треугольник-направленец по АКТИВНОМУ источнику (компас/GPS-курс). Мини-компас только в режиме компаса. Z-order панелями: тайлы < дороги(`roadsPane` 240) < маршрут(`routePane` 250) < трек(overlay) < пак(marker). course-up по GPS-курсу (карта и панель доворачиваются в ландшафт вместе, `bearing = h`), ландшафтный режим (CSS rotation), графики скорости/высоты/ЧСС, импорт GPX/FTB, dead reckoning, placeholder до GPS, карта дорог (дедуп 2м, цвета по вело/пешие/бег), сохранённые маршруты (оверлей-навигация, кнопка-тоггл «М», предпросмотр с профилем высоты, топо/уклон-обводка, тоггл «∡/⛰», высоты по цепочке GPXZ→open-elevation→open-meteo→OpenTopoData), де-джиттер трека (Савицкий-Голей, кнопка «≈» на детали) +- **Трек** — GPS тренировки (запись с HR), карта с компасом (leaflet-rotate). Навигационный «пак» позиции (SVG divIcon): круг цвета трека (на 20% темнее) с белой + 1px чёрной обводкой + треугольник, указывающий ВВЕРХ (карта и так доворачивается активным источником — курс/компас — вверх, поэтому без покадрового поворота: не трясёт). Мини-компас только в режиме компаса. Z-order панелями: тайлы < дороги(`roadsPane` 240) < маршрут(`routePane` 250) < трек(overlay) < пак(marker). course-up по GPS-курсу (карта и панель доворачиваются в ландшафт вместе, `bearing = h`), ландшафтный режим (CSS rotation), графики скорости/высоты/ЧСС, импорт GPX/FTB, dead reckoning, placeholder до GPS, карта дорог (дедуп 2м, цвета по вело/пешие/бег), сохранённые маршруты (оверлей-навигация, кнопка-тоггл «М», предпросмотр с профилем высоты, топо/уклон-обводка, тоггл «∡/⛰», высоты по цепочке GPXZ→open-elevation→open-meteo→OpenTopoData), де-джиттер трека (Савицкий-Голей, кнопка «≈» на детали) - **Добавки** — 41 нутриент (витамины, минералы, жирные кислоты, женьшень, ресвератрол, CoQ10) - **Настройки** — профиль, образ жизни, нормы ЧСС/давления, BLE пульсометр, велосипеды, GPS конфиг (в т.ч. цвет трека `trackColor`, цвета дорог, GPXZ-ключ), OTA обновления, импорт тайлов, карта дорог diff --git a/frontend/js/main.js b/frontend/js/main.js index be73f69..ebce4e6 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -3699,29 +3699,29 @@ async function renderRecordingInline(container) { // rotateWithView keeps the geo-heading correct while the map turns. let puckFill = darkenHex(GPS.config.trackColor || "#bb86fc", 0.8); let meMarker = null, meHasTri = false; - function activeHeading() { - let h = orientMode === "course" ? courseHeading : smoothedHeading; - return (h == null) ? null : h; + // The map already rotates to put the active heading UP (course-up in GPS mode, + // compass-up in compass mode), so travel direction is always screen-up. So the + // puck triangle just points UP — no per-frame rotation. (Computing rotation from + // heading + map bearing desynced and jittered badly.) The icon lives in + // markerPane (norotatePane), which never rotates with the map, so "up" stays up. + function haveHeading() { + return (orientMode === "course" ? courseHeading : smoothedHeading) != null; } function updateMe(ll) { - let h = activeHeading(), wantTri = h != null; + let wantTri = haveHeading(); if (!meMarker) { - meMarker = L.marker(ll, { icon: posPuckIcon(puckFill, wantTri), rotation: h || 0, rotateWithView: true, pane: "markerPane", interactive: false, keyboard: false }).addTo(map); + meMarker = L.marker(ll, { icon: posPuckIcon(puckFill, wantTri), pane: "markerPane", interactive: false, keyboard: false }).addTo(map); meHasTri = wantTri; } else { meMarker.setLatLng(ll); if (wantTri !== meHasTri) { meMarker.setIcon(posPuckIcon(puckFill, wantTri)); meHasTri = wantTri; } - meMarker.options.rotation = h || 0; - meMarker.update(); } } - // Refresh only the direction (rotation/triangle) — called when heading changes. + // Show/hide the direction triangle when a heading becomes (un)available. function refreshMe() { if (!meMarker) return; - let h = activeHeading(), wantTri = h != null; + let wantTri = haveHeading(); if (wantTri !== meHasTri) { meMarker.setIcon(posPuckIcon(puckFill, wantTri)); meHasTri = wantTri; } - meMarker.options.rotation = h || 0; - meMarker.update(); } // Base heading = chosen source. GPS mode rotates by course when we have it, else falls