mirror of https://github.com/rocboss/paopao-ce
Naive UI locks page scroll by setting overflow:hidden on <html>, which drops the classic scrollbar width and still applies margin-right compensation. That reintroduces horizontal jitter when n-dialog / n-modal open or close, on top of the earlier short-to-long content shift. Keep the vertical scrollbar track reserved with overflow-y:scroll !important + scrollbar-gutter, ignore Naive's margin compensation, and freeze background scroll via body position:fixed while the lock is active.pull/728/head
parent
2bb93f8c35
commit
894c23ac84
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Keep document width stable across:
|
||||||
|
* 1) short page → long content (scrollbar appears)
|
||||||
|
* 2) n-dialog / n-modal scroll-lock (Naive UI sets overflow:hidden on <html>)
|
||||||
|
*
|
||||||
|
* Naive UI's useLockHtmlScroll removes the classic scrollbar and optionally
|
||||||
|
* adds margin-right compensation. That fights `scrollbar-gutter` / always-on
|
||||||
|
* overflow-y:scroll and shifts the flex-centered layout plus fixed sidebars
|
||||||
|
* (`left/right: calc(50% + ...)`).
|
||||||
|
*
|
||||||
|
* Strategy:
|
||||||
|
* - CSS keeps the vertical scrollbar track always present (see main.less)
|
||||||
|
* - When Naive marks <html> as locked, freeze body scroll with position:fixed
|
||||||
|
* without ever releasing the scrollbar width
|
||||||
|
*/
|
||||||
|
|
||||||
|
let installed = false;
|
||||||
|
|
||||||
|
export function installStableScrollbar() {
|
||||||
|
if (installed || typeof document === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
installed = true;
|
||||||
|
|
||||||
|
const html = document.documentElement;
|
||||||
|
let frozen = false;
|
||||||
|
let scrollY = 0;
|
||||||
|
|
||||||
|
const isHtmlScrollLocked = () => {
|
||||||
|
const { overflow, overflowY } = html.style;
|
||||||
|
return overflow === 'hidden' || overflowY === 'hidden';
|
||||||
|
};
|
||||||
|
|
||||||
|
const freezeBackgroundScroll = () => {
|
||||||
|
if (frozen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frozen = true;
|
||||||
|
scrollY = window.scrollY;
|
||||||
|
const body = document.body;
|
||||||
|
body.style.position = 'fixed';
|
||||||
|
body.style.top = `-${scrollY}px`;
|
||||||
|
body.style.left = '0';
|
||||||
|
body.style.right = '0';
|
||||||
|
body.style.width = '100%';
|
||||||
|
};
|
||||||
|
|
||||||
|
const unfreezeBackgroundScroll = () => {
|
||||||
|
if (!frozen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frozen = false;
|
||||||
|
const body = document.body;
|
||||||
|
body.style.position = '';
|
||||||
|
body.style.top = '';
|
||||||
|
body.style.left = '';
|
||||||
|
body.style.right = '';
|
||||||
|
body.style.width = '';
|
||||||
|
window.scrollTo(0, scrollY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
// Drop Naive's margin compensation — scrollbar track already reserves width.
|
||||||
|
if (html.style.marginRight && html.style.marginRight !== '0px') {
|
||||||
|
html.style.marginRight = '0px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHtmlScrollLocked()) {
|
||||||
|
freezeBackgroundScroll();
|
||||||
|
} else {
|
||||||
|
unfreezeBackgroundScroll();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new MutationObserver(sync).observe(html, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// In case lock styles are already present
|
||||||
|
sync();
|
||||||
|
}
|
||||||
Loading…
Reference in new issue