diff --git a/public/views/grid-setup.mjs b/public/views/grid-setup.mjs index c3f3533..c120fe1 100644 --- a/public/views/grid-setup.mjs +++ b/public/views/grid-setup.mjs @@ -45,32 +45,37 @@ export class Grid_Setup { const img = new Image(); img.onload = () => { this.#img = img; - const max_w = this.#canvas.parentElement.clientWidth || 800; - const max_h = Math.floor(window.innerHeight * 0.65); - // Canvas fills the available space - this.#css_w = max_w; - this.#css_h = max_h; + // Let CSS determine the width, then read back the actual rendered value. + // Using parentElement.clientWidth directly would include the parent's + // padding, causing css_w to exceed the real content area and making + // getBoundingClientRect() return a different width than css_w. + this.#canvas.style.width = '100%'; + const css_w = this.#canvas.getBoundingClientRect().width || 800; + const css_h = Math.floor(window.innerHeight * 0.65); + + this.#css_w = css_w; + this.#css_h = css_h; // Scale: fit image within canvas with slight padding this.#scale = Math.min( - (max_w * 0.9) / img.width, - (max_h * 0.9) / img.height, + (css_w * 0.9) / img.width, + (css_h * 0.9) / img.height, ); const dpr = window.devicePixelRatio || 1; - this.#canvas.width = this.#css_w * dpr; - this.#canvas.height = this.#css_h * dpr; - this.#canvas.style.width = this.#css_w + 'px'; - this.#canvas.style.height = this.#css_h + 'px'; + this.#canvas.width = css_w * dpr; + this.#canvas.height = css_h * dpr; + this.#canvas.style.width = css_w + 'px'; + this.#canvas.style.height = css_h + 'px'; this.#ctx.scale(dpr, dpr); // Camera: start centered, image fitted within canvas const img_w = img.width * this.#scale; const img_h = img.height * this.#scale; this.#cam_z = 1; - this.#cam_x = (max_w - img_w) / 2; - this.#cam_y = (max_h - img_h) / 2; + this.#cam_x = (css_w - img_w) / 2; + this.#cam_y = (css_h - img_h) / 2; // Default corners: 15% inset in image coords const mx = img.width * 0.15; @@ -159,9 +164,7 @@ export class Grid_Setup { const hit = this.#find_handle(sp); if (hit !== -1) { this.#drag_idx = hit; - if (hit >= 4) { - this.#drag_prev_img = this.#world_to_img(this.#to_world(sp)); - } + this.#drag_prev_img = this.#world_to_img(this.#to_world(sp)); } else { this.#panning = true; this.#pan_last = sp; @@ -173,16 +176,17 @@ export class Grid_Setup { const sp = this.#screen_pos(e); if (this.#drag_idx !== -1) { const img_pos = this.#world_to_img(this.#to_world(sp)); + const dx = img_pos.x - this.#drag_prev_img.x; + const dy = img_pos.y - this.#drag_prev_img.y; if (this.#drag_idx < 4) { - this.#corners[this.#drag_idx] = img_pos; + const i = this.#drag_idx; + this.#corners[i] = { x: this.#corners[i].x + dx, y: this.#corners[i].y + dy }; } else { const [a, b] = Grid_Setup.#MIDPOINT_EDGES[this.#drag_idx - 4]; - const dx = img_pos.x - this.#drag_prev_img.x; - const dy = img_pos.y - this.#drag_prev_img.y; this.#corners[a] = { x: this.#corners[a].x + dx, y: this.#corners[a].y + dy }; this.#corners[b] = { x: this.#corners[b].x + dx, y: this.#corners[b].y + dy }; - this.#drag_prev_img = img_pos; } + this.#drag_prev_img = img_pos; this.#draw(); } else if (this.#panning) { this.#cam_x += sp.x - this.#pan_last.x;