네 번의 재작성으로도 고칠 수 없었던 좌표 공간 버그
Source: Dev.to
The bug
오늘 세션 대부분을 논리적인 문제가 아니라 구조적인 버그를 잡는 데 보냈다.
frandy.dev에는 애니메이션 타임라인 섹션이 있다. 카드들은 가로 트랙에 배치되고, 오른쪽 가장자리를 끌어당겨 폭을 살짝 드러낼 수 있다. 네온 라이트가 세 개의 스파인 트랙을 가로질러 이동하면서 각 카드의 노드 점에 도달하면 깜박인다.
깜박이는 타이밍이 잘못됐는데—스크롤 위치와 카드 상태에 따라 차이가 있었다.
What didn’t work
- 임계값 조정 (크게, 작게, 속도‑종속)
- 방향 가드 추가 (접근할 때만 깜박임)
- 위치 수식 수정 (
scrollLeft, 카드 너비 상태 반영) - 탐지 루프 전체 재구축
이 시도들 중 어느 것도 일관되게 동작하지 않았다.
The coordinate‑space mismatch
이동하는 라이트는 뷰포트 공간—보이는 오버레이의 왼쪽 가장자리로부터 픽셀 단위—에 존재한다.
노드 위치는 카드 공간—전체 트랙(스크롤된 카드 포함)의 왼쪽부터 누적된 픽셀 너비—에서 계산된다.
이 두 좌표계가 일치하는 경우는 오직 하나뿐이다: scroll = 0이고 모든 카드가 열려 있을 때. 그 외의 상태에서는 서로 달라진다.
The fix: measure from the DOM
function measure() {
const oRect = overlay.getBoundingClientRect();
const dots = document.querySelectorAll("[data-tl-node]");
for (const dot of dots) {
const r = dot.getBoundingClientRect();
const x = r.left + r.width / 2 - oRect.left;
if (x overlayWidth + 10) continue; // off‑screen, skip
nodes.push({ x, idx, color });
}
}
- 위치를 직접 계산하는 것을 멈춘다.
getBoundingClientRect()를 사용해 라이트가 이미 사용하고 있는 동일한 좌표계에서 실제 렌더링된 위치를 얻는다.- 스크롤, 카드‑너비 변화, 필터 변화, 리사이즈 시에 다시 측정한다.
- 더티 플래그를 사용해 애니메이션 프레임당 최대 한 번만 측정한다.
Result
라이트가 이제 정확히 노드 점 위에서—매번—깜박인다. 왜냐하면 현실을 읽어들이기 때문이다.
화면에 보이는 것과 좌표 수식이 계속 어긋난다면, 잘못된 좌표 공간을 사용하고 있는 것이다. 브라우저가 이미 올바른 값을 계산했으며,
getBoundingClientRect()가 그 값을 제공한다.
Additional polish
- Theme system – 네 가지 강조 색을 갖는 라이트/다크 모드 전체 지원;
localStorage가 관리자 기본값을 오버라이드한다. 데스크톱 드롭다운, 모바일 슬라이드‑다운 시트가 스크롤 시 BackToTop과 가시성을 교체한다. - Timeline UX – 고무줄 같은 드래그, 스프링 물리, 첫 카드 고정, 패널 열기 더블‑탭, 카드 인덱스 워터마크, 숨김 점 호흡 효과.
- 3‑track spine pulse – 혜성 꼬리, 속도 변동, 호위 오프셋, 회전 + 파동을 동반한 노드 플래시, 미래 카드 색상 폴백, 깔끔한 재시작.
- UI polish – 칩/TabBar 테두리 수정, BackToTop 재정렬, 섹션 패딩 및 네비게이션 높이 확대, 텍스트 불투명도 개선.
Next steps
사이트는 아직 배포되지 않았다. 타임라인 데스크톱은 완성되었다. 다음 단계는 디자인‑전용 작업: 모든 섹션에 대한 모바일 레이아웃을 만든 뒤 관리자 페이지를 진행한다. 새로운 기능은 추가하지 않는다—라이브 전까지 모든 것이 보기 좋게 다듬는 것이 목표다.