Skip to content

fix(#141665): keep screenshot markers glued to content when the page scrolls before submit#128

Merged
boehlerlukas merged 2 commits into
masterfrom
gleap/141665-marker-scroll-drift
Jul 24, 2026
Merged

fix(#141665): keep screenshot markers glued to content when the page scrolls before submit#128
boehlerlukas merged 2 commits into
masterfrom
gleap/141665-marker-scroll-drift

Conversation

@boehlerlukas

Copy link
Copy Markdown
Contributor

Problem

Gleap ticket #141665 (reported by MEINbusiness): a bug-report screenshot showed the customer's marker rectangle around the wrong content. The end user had marked the "Zuschlag" row, but in the rendered screenshot the rectangle sat ~120px lower, around an email address row.

Root cause

The capture editor's annotation SVG (.bb-capture-svg) is position: fixed and drawings are stored in viewport coordinates (clientX/clientY). The DOM snapshot is serialized later, at submit time. If the page scrolls between drawing and submit, the snapshot records the new scroll position (bb-scrolltop / snapshotPosition), and the replayed screenshot shows the marks shifted by exactly the scroll delta.

Scrolling can happen after drawing because:

  • GleapScrollStopper blocks wheel/touch/arrow keys but not scrollbar dragging, and
  • scrolling is fully re-enabled after clicking Next, while the preview overlay stays fixed on screen.

Verified against the actual snapshot of the affected ticket: the marker aligns exactly with the intended row once the 120px scroll delta (draw-time scroll 1116 vs. submit-time scroll 1236) is compensated; re-rendered through Server-HTMLtoImage locally to confirm (renderer itself is not at fault).

Fix

ScreenDrawer now records the scroll position at drawing start and counter-shifts the overlay on every window scroll via an inline transform: translate(...):

  • Live: marks stay glued to the content they were drawn on, both during marking (scrollbar drag) and while the feedback form is open.
  • Snapshot: the inline transform serializes into the captured DOM, so the rendered screenshot stays aligned with no renderer changes needed.
  • Mouse/touch coordinates are mapped into the shifted overlay space, so drawing stays under the cursor after a mid-marking scroll.
  • Screen-recording drawings remain viewport-fixed to match the video.
  • The tracker survives destroy() (drawing finished, form open) and is released in GleapMarkerManager.clear().

Deliberately not document-anchored at absolute coordinates: the replay layout can compress vs. the user's browser (font metrics), so absolute document coordinates accumulate layout drift from the page top. The scroll-delta transform only compensates the actual scroll movement and inherits none of that (verified empirically against the real snapshot).

Tests

  • New src/ScreenDrawer.test.js covering transform tracking, coordinate mapping after mid-drawing scroll, disabled tracking for recordings, and tracker lifecycle (4 tests).
  • Full suite: 214 passed. npm run build clean.

🤖 Generated with Claude Code

boehlerlukas and others added 2 commits July 24, 2026 13:41
…lls before submit

The capture editor's annotation SVG is position: fixed and drawings are
stored in viewport coordinates. When the page scrolled between drawing
and the DOM snapshot (scrollbar drag while marking, or scrolling while
the feedback form is open after Next), the snapshot recorded the new
scroll position and the rendered screenshot showed the marks shifted by
exactly that scroll delta.

ScreenDrawer now tracks window scrolling relative to the position at
drawing start and counter-shifts the overlay via an inline transform,
which also serializes into the DOM snapshot so the replayed screenshot
stays aligned. Mouse/touch coordinates are mapped into the shifted
overlay space so drawing stays under the cursor after a mid-marking
scroll. Screen-recording drawings remain viewport-fixed to match the
video. The tracker survives ScreenDrawer.destroy() (preview overlay
keeps tracking while the form is open) and is released when the capture
editor is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anchoring to the drawing-start scroll position left the top strip of the
viewport uncovered (and coordinates negative/clipped) when the user
scrollbar-dragged above the anchor while marking. Working in document
coordinates with transform: translate(-scrollX, -scrollY) keeps the
fixed overlay covering the viewport in both scroll directions while
producing the identical serialized geometry (stored coord includes the
draw-time scroll, the serialized transform subtracts the submit-time
scroll). Also tracks min-width against scrollWidth for horizontally
scrollable pages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@boehlerlukas

Copy link
Copy Markdown
Contributor Author

Hardened after a self-review: the first version anchored the counter-shift to the drawing-start scroll position, which left the top strip of the viewport undrawable (and produced negative, clipped coordinates) if the user scrollbar-dragged above that anchor while marking. The overlay now works in document coordinates with transform: translate(-scrollX, -scrollY) — same serialized replay geometry (stored coordinate carries the draw-time scroll, serialized transform subtracts the submit-time scroll), but full viewport coverage in both scroll directions and no negative coordinates. Tests updated (214 passing).

@boehlerlukas
boehlerlukas merged commit 86f0551 into master Jul 24, 2026
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