Skip to content

fix(explorer): stop the canvas growing vertically on every hover - #21

Merged
jeminkachhadiya merged 1 commit into
mainfrom
feat/sfm-analysis-sdk
Aug 25, 2026
Merged

fix(explorer): stop the canvas growing vertically on every hover#21
jeminkachhadiya merged 1 commit into
mainfrom
feat/sfm-analysis-sdk

Conversation

@jeminkachhadiya

Copy link
Copy Markdown
Contributor

The real cause of the reported "hovering zooms the graph vertically" bug, and a genuine defect in the explorer's own code -- not a browser pinch-zoom behavior, which is what the two preceding commits wrongly guessed at. Those guards are reverted here: they addressed a problem that wasn't occurring, and both were actively harmful, stripping the browser's standard Ctrl+scroll and touch-pinch zoom affordance from the widget.

canvas.height = N is a reflected IDL attribute: assigning it also rewrites the element's "height" content attribute. sizeCanvas() read getAttribute("height") back as its source of truth for the CSS height, so every redraw re-multiplied the already-scaled backing-store height by devicePixelRatio again. mouseleave calls drawMain(), so a single hover in-and-out doubled the canvas. Measured at dpr 2, hover alone, no zoom input: 230 -> 460 -> 920 -> 1840 -> 3680 -> 7360 px.

At devicePixelRatio == 1 the multiply is a no-op and the bug cannot be observed at all -- which is why it shipped, and why the earlier headless checks (run at the default scale factor of 1) came back clean and were wrongly read as clearing the chart's own math.

Fix: capture each canvas's intended CSS height once into a memoized variable and never re-read the attribute that sizeCanvas itself writes.

Verified with headless Chromium at devicePixelRatio 1, 2 and 3: main and overview canvas heights are byte-stable across 20 hover in/out cycles, 15 wheel zooms, reset, and window resizes; the backing store is exactly cssH * dpr at each scale (crisp, not oversized); horizontal zoom still works; the second widget on the page is independent and equally stable. The added regression test asserts the structure (height memoized, never re-read) rather than trying to observe growth, since the bug is invisible at dpr 1 -- confirmed it fails when the old line is reinstated and passes when it isn't.

Suite: 268 passed, 1 skipped, 0 failures.

The real cause of the reported "hovering zooms the graph vertically"
bug, and a genuine defect in the explorer's own code -- not a browser
pinch-zoom behavior, which is what the two preceding commits wrongly
guessed at. Those guards are reverted here: they addressed a problem
that wasn't occurring, and both were actively harmful, stripping the
browser's standard Ctrl+scroll and touch-pinch zoom affordance from
the widget.

canvas.height = N is a *reflected* IDL attribute: assigning it also
rewrites the element's "height" content attribute. sizeCanvas() read
getAttribute("height") back as its source of truth for the CSS height,
so every redraw re-multiplied the already-scaled backing-store height
by devicePixelRatio again. mouseleave calls drawMain(), so a single
hover in-and-out doubled the canvas. Measured at dpr 2, hover alone,
no zoom input: 230 -> 460 -> 920 -> 1840 -> 3680 -> 7360 px.

At devicePixelRatio == 1 the multiply is a no-op and the bug cannot be
observed at all -- which is why it shipped, and why the earlier
headless checks (run at the default scale factor of 1) came back clean
and were wrongly read as clearing the chart's own math.

Fix: capture each canvas's intended CSS height once into a memoized
variable and never re-read the attribute that sizeCanvas itself writes.

Verified with headless Chromium at devicePixelRatio 1, 2 and 3: main
and overview canvas heights are byte-stable across 20 hover in/out
cycles, 15 wheel zooms, reset, and window resizes; the backing store is
exactly cssH * dpr at each scale (crisp, not oversized); horizontal
zoom still works; the second widget on the page is independent and
equally stable. The added regression test asserts the structure (height
memoized, never re-read) rather than trying to observe growth, since
the bug is invisible at dpr 1 -- confirmed it fails when the old line
is reinstated and passes when it isn't.

Suite: 268 passed, 1 skipped, 0 failures.
@jeminkachhadiya
jeminkachhadiya merged commit 838b9d3 into main Aug 25, 2026
12 of 18 checks passed
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