Skip to content

Fix lens drift when multiple in-flow lenses collapse the snapshot#7

Open
codedgar wants to merge 1 commit into
naughtyduk:mainfrom
codedgar:fix/lens-snapshot-drift
Open

Fix lens drift when multiple in-flow lenses collapse the snapshot#7
codedgar wants to merge 1 commit into
naughtyduk:mainfrom
codedgar:fix/lens-snapshot-drift

Conversation

@codedgar

Copy link
Copy Markdown

Summary

Fixes a snapshot/coord mismatch in captureSnapshot that caused every .liquidGL lens to refract the wrong region when multiple lenses sat in normal document flow on a long page. The misalignment compounded with depth, each additional lens above contributed to the offset.

Root cause

captureSnapshot passed lens elements into html2canvas's ignoreElements callback. html2canvas honors ignoreElements by setting display: none on those elements in its cloned DOM, which collapses them out of layout. Any element below a collapsed lens shifts up in the snapshot by that lens's height.

Lens sampling math in _renderLens (scripts/liquidGL.js:686–688) reads the lens position from the live DOM:

const docY = rect.top - this.snapshotTarget.getBoundingClientRect().top;
const topUV = (docY * this.scaleFactor) / this.textureHeight;

Live coords no longer match snapshot coords, so the UV sample lands in the wrong place. With N lenses above a given lens, the cumulative collapse is N × lens-height, so the offset grows the deeper into the page you go. Lenses with position: absolute mostly hid the bug because they don't contribute to flow; lenses in normal flow (block-level) make it obvious.

Fix

Stop using ignoreElements to remove lens elements. Instead:

  1. Tag each live lens element with a data-liquidgl-hide attribute before calling html2canvas (attribute mutation only — no visual repaint).
  2. Pass an onclone(clonedDoc) handler that finds those tagged elements in the clone and sets visibility: hidden.
  3. Remove the attribute after capture.

visibility: hidden preserves the element's box in layout but doesn't paint its content — so the snapshot doesn't double-render the lens, and live-DOM coords match snapshot coords exactly. The data-attribute approach matches the existing data-liquid-ignore pattern already in ignoreElementsFunc.

Reproducing

A long page with 4+ block-level .liquidGL elements interspersed with content (e.g. between sections) reproduces the bug on main. Each lens refracts content from a position that drifts further from its true backdrop the lower it sits. The first lens is also affected because its own collapse shifts content positioned within its parent gap.

Try the demo

Open /demos/comparison.html — side-by-side panes showing the upstream build (left) vs the patched fork (right) on the same scene. Scroll either side; the other follows. The patched pane keeps every lens locked to its target stripe.

Test plan

  • Existing demos (demos/demo-1.htmldemos/demo-5.html) still render correctly — single-lens and absolute-positioned lens cases remain unchanged.
  • On a long page with multiple in-flow .liquidGL lenses, every lens refracts the content directly behind it at every scroll position.
  • Scroll behavior is unchanged (no flicker on the live DOM during snapshot capture, since the attribute mutation doesn't repaint).
  • Snapshot recapture on resize/scroll continues to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant