YES canvas: size off its own container rect, not window.innerHeight
Deploy / deploy (push) Successful in 34s

setupCanvas() measured window.innerWidth/innerHeight directly, then a
resize listener gated on innerWidth changing (guessing which resize
events were "real" vs mobile Safari's address-bar animation). That
measurement had nothing to do with .hero-yes's actual CSS height, so
the canvas and its box could end up disagreeing - which is what was
producing the observed jump in content below the hero on scroll, not
the choice of viewport unit on its own.

Switched to ResizeObserver on .hero-canvas (which tracks .hero-yes via
inset:0), using its contentRect directly. This also drops the
innerWidth-gating heuristic entirely: a fixed-height box never fires
a ResizeObserver callback during the toolbar animation in the first
place, so there's nothing to gate.

Also updated the mouse/touch position normalization to use the
canvas's own displayWidth/displayHeight instead of
window.innerWidth/innerHeight, for the same reason - and fixed the
.hero-yes comment, which still described the lvh reasoning from a
prior attempt after the height value itself had been changed back to
svh directly on origin/main.
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-06 21:22:00 +02:00
parent cc5174813c
commit d7f280e681
2 changed files with 41 additions and 31 deletions
+30 -20
View File
@@ -42,6 +42,7 @@ export class RasterizedYES {
this.wiggleAmount = 0.5;
this.setupCanvas();
this.setupResizeObserver();
this.setupMouseTracking();
this.setupClickHandler();
this.rasterizeText();
@@ -51,15 +52,24 @@ export class RasterizedYES {
stop() {
this.destroyed = true;
if (this._resizeHandler) {
window.removeEventListener('resize', this._resizeHandler);
if (this._resizeObserver) {
this._resizeObserver.disconnect();
}
}
// Measures the canvases' own containing box (.hero-canvas, which
// tracks .hero-yes via inset:0) rather than window.innerWidth/
// innerHeight. window.innerHeight is the live viewport - it moves
// as mobile Safari's address bar hides/shows on scroll, which
// doesn't itself match whatever fixed value .hero-yes's CSS height
// is pinned to. Sizing the canvas off the box's own rect instead
// means the two can never disagree, whichever viewport unit ends
// up governing that box.
setupCanvas() {
const pixelRatio = window.devicePixelRatio || 1;
const width = window.innerWidth;
const height = window.innerHeight;
const rect = this.rasterCanvas.parentElement.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
this.rasterCanvas.style.width = width + 'px';
this.rasterCanvas.style.height = height + 'px';
@@ -77,27 +87,27 @@ export class RasterizedYES {
this.displayWidth = width;
this.displayHeight = height;
this.pixelRatio = pixelRatio;
}
if (!this._resizeHandler) {
// Mobile browsers change window.innerHeight (not width) as
// the address bar hides/shows during scroll, firing a
// `resize` with no real layout change to react to.
// Genuine resizes (orientation change, desktop window
// drag) always change the width too, so gate the redraw
// on that instead of reacting to every resize event.
this._resizeHandler = () => {
if (window.innerWidth === this.displayWidth) return;
this.setupCanvas();
this.rasterizeText();
};
window.addEventListener('resize', this._resizeHandler);
}
// ResizeObserver reacts to the container's box actually changing
// size - a fixed-height box (svh, lvh, or plain px) never fires
// this at all during the address-bar animation, so there's no
// need for the old "only redraw if width changed" heuristic that
// guessed at which resize events were real.
setupResizeObserver() {
this._resizeObserver = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
if (width === this.displayWidth && height === this.displayHeight) return;
this.setupCanvas();
this.rasterizeText();
});
this._resizeObserver.observe(this.rasterCanvas.parentElement);
}
setupMouseTracking() {
const updatePosition = (clientX, clientY) => {
this.mouseX = clientX / window.innerWidth;
this.mouseY = clientY / window.innerHeight;
this.mouseX = clientX / this.displayWidth;
this.mouseY = clientY / this.displayHeight;
this.containmentStrength = 0.1 + (this.mouseX * 0.9);
this.wiggleAmount = 0.1 + (this.mouseY * 1.9);
};