fix(zoom): use transform:scale instead of CSS zoom to kill map tile seams
CSS `zoom` re-lays-out and pixel-rounds each element, which opened ~1px seams between map tiles (a faint grid over the map when zoomed). Switch the persistent UI zoom to `transform: scale(z)` from the app root's top-left, with the root counter-sized to (100/z)vw × (100/z)vh so it reflows to fill and scales back to the window. transform scales the subtree as one composited layer, so tiles stay seamless. Removed the now-unneeded tile-overlap CSS hack.
This commit is contained in:
+14
-17
@@ -845,31 +845,28 @@ export default function App() {
|
|||||||
useEffect(() => { wkEsmRef.current = wkEsm; }, [wkEsm]);
|
useEffect(() => { wkEsmRef.current = wkEsm; }, [wkEsm]);
|
||||||
|
|
||||||
// Persistent Ctrl+wheel zoom. The native WebView2 Ctrl+wheel zoom isn't saved,
|
// Persistent Ctrl+wheel zoom. The native WebView2 Ctrl+wheel zoom isn't saved,
|
||||||
// so we run our own: CSS `zoom` on the app root, factor stored in localStorage
|
// so we run our own, factor stored in localStorage and restored at startup;
|
||||||
// and restored at startup. Ctrl+0 resets to 100%.
|
// Ctrl+0 resets to 100%.
|
||||||
//
|
//
|
||||||
// `zoom` scales the element uniformly (grid included) but does NOT rescale
|
// We use `transform: scale` (NOT CSS `zoom`): `zoom` re-lays-out and rounds each
|
||||||
// viewport units, so a 100vh/100vw root would leave an empty strip when zoomed
|
// element to the pixel grid, which opens ~1px seams between map tiles. `transform`
|
||||||
// out. We counter that by sizing the root to (100/z)vw × (100/z)vh so that,
|
// scales the whole subtree as one composited layer, so tiles stay seamless. The
|
||||||
// after the zoom multiplies it back down, it exactly fills the window.
|
// app root is counter-sized to (100/z)vw × (100/z)vh and scaled from its top-left
|
||||||
|
// by z, so it reflows to fill that larger box and then scales back to exactly the
|
||||||
|
// window (more content visible when zoomed out, like a real browser zoom).
|
||||||
const appRootRef = useRef<HTMLDivElement>(null);
|
const appRootRef = useRef<HTMLDivElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const KEY = 'opslog.uiZoom';
|
const KEY = 'opslog.uiZoom';
|
||||||
let z = parseFloat(localStorage.getItem(KEY) || '1');
|
let z = parseFloat(localStorage.getItem(KEY) || '1');
|
||||||
if (!Number.isFinite(z) || z <= 0) z = 1;
|
if (!Number.isFinite(z) || z <= 0) z = 1;
|
||||||
const apply = () => {
|
const apply = () => {
|
||||||
// Zoom the whole document (so portalled menus/modals/tooltips scale too)…
|
|
||||||
(document.documentElement.style as any).zoom = String(z);
|
|
||||||
// Flag the zoom state so the Leaflet tile-seam fix (style.css) only kicks in
|
|
||||||
// when actually zoomed (fractional scaling opens 1px gaps between tiles).
|
|
||||||
document.documentElement.setAttribute('data-uizoom', String(z));
|
|
||||||
// …and counter-size the app root: `zoom` doesn't rescale vh/vw, so a 100vh
|
|
||||||
// root leaves an empty strip when zoomed out. (100/z)vh zoomed by z = 100vh.
|
|
||||||
const el = appRootRef.current;
|
const el = appRootRef.current;
|
||||||
if (el) {
|
if (!el) return;
|
||||||
el.style.width = `${100 / z}vw`;
|
el.style.transformOrigin = '0 0';
|
||||||
el.style.height = `${100 / z}vh`;
|
el.style.transform = z === 1 ? '' : `scale(${z})`;
|
||||||
}
|
el.style.width = `${100 / z}vw`;
|
||||||
|
el.style.height = `${100 / z}vh`;
|
||||||
|
document.documentElement.setAttribute('data-uizoom', String(z));
|
||||||
};
|
};
|
||||||
apply();
|
apply();
|
||||||
const onWheel = (e: WheelEvent) => {
|
const onWheel = (e: WheelEvent) => {
|
||||||
|
|||||||
@@ -652,13 +652,3 @@
|
|||||||
border: 2px solid var(--background);
|
border: 2px solid var(--background);
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
|
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
|
||||||
|
|
||||||
/* When the UI is CSS-zoomed (our persistent Ctrl+wheel zoom), map tiles are
|
|
||||||
scaled by a fractional factor, which opens ~1px seams between adjacent tiles
|
|
||||||
(visible as a faint grid over the map). Enlarging each 256px tile by 1px makes
|
|
||||||
neighbours overlap just enough to hide the seams. Scoped to the zoomed state so
|
|
||||||
it never touches the crisp 100% view. */
|
|
||||||
html[data-uizoom]:not([data-uizoom="1"]) .leaflet-tile {
|
|
||||||
width: 257px !important;
|
|
||||||
height: 257px !important;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user