Skip to content

Commit 37532e2

Browse files
Ryan Williamsclaude
andcommitted
www/map: don't fade bins on pan — only on an actual res change
The loading fade compared the *rendered* res (`currRes`, which can be budget-coarsened below the target) against the picker's `targetRes`, so whenever coarsening was active the two stayed unequal and every pan refetch faded/desaturated the already-drawn bins. Track the target res of the currently-shown data in a ref and fade only when the live target res differs from it — i.e. a zoom that crossed a level boundary. Pure pans (same res, new viewport) keep the bins fully opaque; the corner `RefetchSpinner` still signals the in-flight fetch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5196ff9 commit 37532e2

1 file changed

Lines changed: 20 additions & 12 deletions

File tree

www/src/map/CrashMapSection.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,24 @@ export function CrashMapSection({
448448
}
449449
}, [apiResult, v2Manifest])
450450

451+
// Level-change detection for the loading fade. Fade/desaturate the prior
452+
// bins only when the picker's *target* res actually changed (a zoom that
453+
// crossed a level boundary) — never on a pure pan (same res, new
454+
// viewport; the bins mostly stay put, so muting them is just noise). We
455+
// compare the live target res against the target res of the data
456+
// currently shown (tracked in a ref), so budget-coarsening (rendered res
457+
// < target res) doesn't read as a level change on every refetch.
458+
const targetRes = pickerInfo?.levels.find(l => l.isCurrent)?.res ?? null
459+
const shownTargetRes = useRef<number | null>(null)
460+
useEffect(() => {
461+
if (result.status === "ready" && !result.refetching && targetRes !== null) {
462+
shownTargetRes.current = targetRes
463+
}
464+
}, [result, targetRes])
465+
const resChanging = result.status === "ready" && result.refetching
466+
&& shownTargetRes.current !== null && targetRes !== null
467+
&& shownTargetRes.current !== targetRes
468+
451469
// Worker handles budget enforcement via `?maxCells=`: dense shards
452470
// come back at a coarser res than requested. Client-side fallback
453471
// here is just a safety net (cumulative across shards may still
@@ -659,16 +677,6 @@ export function CrashMapSection({
659677
)}
660678
{result.status === "loading" && <LoadingOverlay theme={actualTheme} />}
661679
{result.status === "ready" && (() => {
662-
// Fade + desaturate only when the incoming res differs from
663-
// what's currently on screen. Same-res pans just clip
664-
// existing bins to a new viewport — muting them would
665-
// misleadingly suggest a bigger change is coming. The
666-
// corner `<RefetchSpinner>` still signals "fetch in flight"
667-
// for pan-only reloads.
668-
const currRes = renderHexes?.res
669-
?? (result.data.length ? getResolution(result.data[0].h3) : null)
670-
const targetRes = pickerInfo?.levels.find(l => l.isCurrent)?.res ?? null
671-
const resChanging = currRes !== null && targetRes !== null && currRes !== targetRes
672680
return (
673681
<Suspense fallback={<LoadingOverlay theme={actualTheme} />}>
674682
<CrashMap
@@ -692,8 +700,8 @@ export function CrashMapSection({
692700
coverCells={drawerOpen && debugOpen ? apiResult.plan?.cover ?? null : null}
693701
viz={viz}
694702
circleRadiusPx={circleRadiusPxValue}
695-
hexOpacity={result.refetching && resChanging ? 0.35 : 1}
696-
hexDesaturate={result.refetching && resChanging ? 0.55 : 0}
703+
hexOpacity={resChanging ? 0.35 : 1}
704+
hexDesaturate={resChanging ? 0.55 : 0}
697705
/>
698706
</Suspense>
699707
)

0 commit comments

Comments
 (0)