Skip to content

Option to reconcile viewport labels only on zoom end - #1599

Open
barrulus wants to merge 1 commit into
Azgaar:masterfrom
barrulus:fix/zoom-end-gesture-reconcile
Open

Option to reconcile viewport labels only on zoom end#1599
barrulus wants to merge 1 commit into
Azgaar:masterfrom
barrulus:fix/zoom-end-gesture-reconcile

Conversation

@barrulus

Copy link
Copy Markdown
Collaborator

Currently every guard-band escape during a zoom or pan re-runs label materialization and the collision reflow pass, so a long gesture recalculates the labels many times over. Mid-gesture the already-materialized content just rides the viewbox transform, so those intermediate reconciles buy nothing visually - this moves the reconcile to the end of the gesture, running it once.

Two pitfalls shaped the implementation (both learned the hard way on my fork, which has been running this change):

  • A pending animation frame is not a usable "transform changed" signal. Wheel gestures end on d3's idle timeout, ~150ms after the last zoom event, long after the frame was consumed - gating the end-of-gesture render on frameId !== null freezes labels for every human-paced gesture. A per-gesture flag set in the zoom handler tracks the change directly.
  • A plain click is a zero-movement "gesture". Rendering unconditionally on its mouseup churns the label/icon nodes between mousedown and click dispatch, and the browser swallows the click (down and up must hit the same element). The flag never gets set for a zero-movement gesture, so clicks are untouched.

The included e2e drives a real wheel gesture via page.mouse.wheel. That matters: setMapZoom()-style programmatic zooms dispatch end synchronously while the frame is still pending, so they cannot detect the frozen-labels failure mode - the existing suite stayed green through it.

Verified: label materialization after a wheel gesture is identical to the per-frame behavior on the same seed (headless comparison), tsc clean, 378/378 unit tests, new e2e green.

@netlify

netlify Bot commented Aug 22, 2026

Copy link
Copy Markdown

Deploy Preview for afmg ready!

Name Link
🔨 Latest commit 32eb403
🔍 Latest deploy log https://app.netlify.com/projects/afmg/deploys/6a89e07698e4c30008557449
😎 Deploy Preview https://deploy-preview-1599--afmg.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Per-frame ViewportLayers.schedule() re-ran label materialization and the
collision reflow pass on every guard-band escape, so long zooms and pans
recalculated many times over. Mid-gesture the materialized content just
rides the viewbox transform; the reconcile now happens once, when the
gesture settles.

A per-gesture flag tells a real gesture from a plain click: wheel
gestures end on d3's idle timeout, after the last frame is consumed, so
a pending frame cannot serve as the signal - and rendering on a click's
zero-movement gesture would churn the DOM between mousedown and click
dispatch and swallow the click. The new e2e drives a real wheel gesture,
which setMapZoom-based tests cannot cover (d3 dispatches their end
synchronously).
@Azgaar

Azgaar commented Aug 23, 2026

Copy link
Copy Markdown
Owner

The viewport reconciliation mid gesture is for a smoother transition and it actually looks nicer when labels pop up mid transition more naturally, not all at once at the end. And if we want them to only render at the end of transition, we can just remove a single line, we don't need the viewChangedInGesture guard.

I tested the preview, it looks much worse for me, especially on zooming out.

@barrulus

Copy link
Copy Markdown
Collaborator Author

The viewport reconciliation mid gesture is for a smoother transition and it actually looks nicer when labels pop up mid transition more naturally, not all at once at the end. And if we want them to only render at the end of transition, we can just remove a single line, we don't need the viewChangedInGesture guard.

I tested the preview, it looks much worse for me, especially on zooming out.

I think this might be something I can live with for now on my fork as a short term performance improvement. I do not believe it is adding value to AFMG based on your statement. I will continue to explore ways to improve efficiency and will take this design direction on baord :)

@barrulus barrulus closed this Aug 23, 2026
@Azgaar

Azgaar commented Aug 23, 2026

Copy link
Copy Markdown
Owner

@barrulus, it can be made optional. We have optimizedRendering boolean, but it can be a whole set of options for users. So some may toggle mid-transition rendering off for a slightly better performance. It's a tradeoff. I would like to have it on by default, but some users like options

@barrulus

Copy link
Copy Markdown
Collaborator Author

@barrulus, it can be made optional. We have optimizedRendering boolean, but it can be a whole set of options for users. So some may toggle mid-transition rendering off for a slightly better performance. It's a tradeoff. I would like to have it on by default, but some users like options

ok, has there been any work opn deciding what options to make available to users?

@barrulus barrulus reopened this Aug 23, 2026
@Azgaar

Azgaar commented Aug 23, 2026

Copy link
Copy Markdown
Owner

ok, has there been any work opn deciding what options to make available to users?

No, but instead of a generic mode, we can make each option a separate one named based on what it actually controls. Check how it's used now.

@Azgaar Azgaar changed the title perf: reconcile viewport labels once per zoom gesture, at its end Option to reconcile viewport labels only on zoom end Aug 25, 2026
@Azgaar
Azgaar self-requested a review August 25, 2026 14:50
@Azgaar Azgaar moved this from Backlog to In progress in FMG dev board Aug 31, 2026
@Azgaar Azgaar moved this from In progress to Ready in FMG dev board Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Approved

Development

Successfully merging this pull request may close these issues.

3 participants