Files
onboarding-project/docs/worklog/15-joystick-redesign.md
T
2026-08-14 15:03:43 +03:00

4.1 KiB

Джойстик: редизайн під референс + два реальні баги (2026-08-13)

Референс: прозора середина, градієнтний бордер, помаранчева точка пальця з підсвіткою навколо. JoystickC.init() (SDK-обгортка nipplejs) приймає будь-які опції nipplejs напряму, включно з color: {back, front} — кожен рядок йде прямо в background відповідного div, підтримує градієнти й навіть var()/calc() (живі CSS-змінні всередині inline style реально оновлюються).

Фінальна реалізація (beforeResourcesLoadedCb.ts + main.css):

  • back (кільце) — conic-gradient(from var(--joy-angle), ...), базовий колір однотонний темний, підсвітка (тон точки пальця) лише у широкій дузі навпроти пальця, з calc()-інтерполяцією RGB-каналів (не альфи!) за --joy-force.
  • front (точка) — суцільний плоский колір, без градієнта/тіні.
  • Форма кільця (прозора середина) — mask-image: radial-gradient(...) з абсолютними px, не відсотками — щоб зміна size (радіус зони) не розтягувала товщину бордера пропорційно.
  • Розмір точки пальця (.front) — фіксований px-оверрайд, повністю відв'язаний від size (nipplejs хардкодить точку як size/2, тож збільшення радіуса зони інакше тягнуло за собою і точку).
  • JoystickC.onJoysticMove — окрема підписка (SDK підтримує кілька делегатів на подію, PlayerC підписаний окремо на той самий івент для руху) ставить --joy-angle/--joy-force на instance.ui.el щокадру.

⚠️ Знайдений і виправлений баг: підсвітка на 90° проти стрілки від реального положення пальця. Math.atan2(y, x) на скрін-координатах (y вниз) і CSS conic-gradient(from Xdeg, ...) обидва рахують по годинниковій, але з різною точкою нуля: atan2 — схід (3 година), conic-gradient — північ (12 година). Фікс — плюс 90° один раз, не свап знаку і не переписування напрямку градієнта.

⚠️ Знайдений і виправлений баг: підсвітка не зупинялась на межі зони, а продовжувала змінюватись, коли палець вели далі за край. Трасування в самому nipplejs (Collection.ts): force рахується з СИРОЇ, необмеженої дистанції дотику (dist / size), тоді як frontPosition (сама видима точка й кут вище) — з ОБМЕЖЕНОЇ. За краєм кільця force продовжує необмежено зростати, хоча точка вже візуально запінена на місці — усі calc() розраховані на force в межах 0..1. Це ж переповнення й давало жовтуватий відтінок (RGB-канали переїжджали за цільовий колір). Фікс — один Math.min(Math.max(force, 0), 1) перед записом у CSS-змінну, а не clamp у кожному calc() окремо.

Розмір: size (радіус зони) підняли на +50%, потім усі елементи (зона+точка+товщина кільця) зменшені на -30% від того — фінальні числа: size: 105, точка 35px/-17.5px margin, кільце 43.4/44.8/46.9/48.3px.