Skip to content

Commit d7aa352

Browse files
mbesserclaude
andcommitted
fix: correct Firefox canvas shear and portrait positioning
Float getBoundingClientRect values in Firefox caused a progressive x-offset per row when used as pixel stride, producing a parallelogram distortion. Fixed by rounding W/H to integers and using imageData.width as the stride. Portrait left boundary now reads from getComputedStyle grid column widths instead of getBoundingClientRect on a zero-size grid item (unreliable in Firefox). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e7a13a7 commit d7aa352

2 files changed

Lines changed: 21 additions & 10 deletions

File tree

assets/sass/_base.scss

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,9 @@ body {
6262
.home__face {
6363
position: absolute;
6464
top: 0;
65+
left: 0;
6566
right: 0;
6667
bottom: -100px;
67-
width: 100%;
68-
height: 100%;
6968
background: url('/img/matti.jpg') no-repeat 50% 100%;
7069
background-size: 60%;
7170

index.html

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -930,14 +930,24 @@ <h2 class="heading-large about__heading reveal" id="about-heading">
930930

931931
function sampleAndBuild() {
932932
var rect = canvas.parentElement.getBoundingClientRect();
933-
W = rect.width || window.innerWidth;
934-
H = rect.height || window.innerHeight;
933+
// Round to integers — Firefox reports fractional getBoundingClientRect values.
934+
// Using a float as both canvas.width (which floors) and as row stride in the
935+
// pixel index formula causes a progressive x-offset per row (parallelogram shear).
936+
W = Math.round(rect.width || window.innerWidth);
937+
H = Math.round(rect.height || window.innerHeight);
935938
if (!W || !H) return false;
936939
canvas.width = W; canvas.height = H;
937940

938-
// Portrait starts at col 3 left edge (cols 3–6), overlapping the text in col 3
939-
var col3Anchor = canvas.parentElement.querySelector('.hero__col3-anchor');
940-
var portraitLeft = col3Anchor ? col3Anchor.getBoundingClientRect().left : W * 0.3;
941+
// Portrait left boundary: read actual computed column widths from the browser.
942+
var heroStyle = window.getComputedStyle(canvas.parentElement);
943+
var colStr = heroStyle.gridTemplateColumns || '';
944+
var gapPx = parseFloat(heroStyle.columnGap || heroStyle.gridColumnGap) || 0;
945+
var cols = (colStr && colStr !== 'none')
946+
? colStr.trim().split(/\s+/).map(parseFloat).filter(function(n) { return n > 0; })
947+
: [];
948+
var portraitLeft = cols.length >= 2
949+
? cols[0] + gapPx + cols[1] + gapPx // left edge of grid col 3
950+
: W * 0.35;
941951

942952
var portraitScale = W < 600 ? 0.62 : W < 1024 ? 0.70 : 0.80;
943953
var imgAspect = img.width / img.height;
@@ -957,16 +967,18 @@ <h2 class="heading-large about__heading reveal" id="about-heading">
957967
offCtx.fillStyle = '#ffffff';
958968
offCtx.fillRect(0, 0, W, H);
959969
offCtx.drawImage(img, drawX, drawY, drawW, drawH);
960-
var imgData;
961-
try { imgData = offCtx.getImageData(0, 0, W, H).data; } catch(e) { return false; }
970+
var imageData;
971+
try { imageData = offCtx.getImageData(0, 0, W, H); } catch(e) { return false; }
972+
var imgData = imageData.data;
973+
var dataW = imageData.width; // use actual data stride, never a float
962974

963975
dots = [];
964976
var halfSpacing = dotSpacing / 2;
965977
for (var y = halfSpacing; y < H; y += dotSpacing) {
966978
for (var x = halfSpacing; x < W; x += dotSpacing) {
967979
var sx = Math.min(Math.round(x), W - 1);
968980
var sy = Math.min(Math.round(y), H - 1);
969-
var idx = (sy * W + sx) * 4;
981+
var idx = (sy * dataW + sx) * 4;
970982
var r = imgData[idx], g = imgData[idx+1], b = imgData[idx+2];
971983
var brightness = (0.299*r + 0.587*g + 0.114*b) / 255;
972984
if (brightness > BRIGHT_CUT) continue;

0 commit comments

Comments
 (0)