fix:解决移动端 Dialog 滚动问题

This commit is contained in:
hzm
2026-03-12 21:53:11 +08:00
parent 7953b906a5
commit 8849b547ce
3 changed files with 49 additions and 27 deletions

View File

@@ -6,11 +6,38 @@ import { Dialog as DialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import {CloseIcon} from "@/app/components/Icons";
import { useBodyScrollLock } from "../../app/hooks/useBodyScrollLock";
function Dialog({
open: openProp,
defaultOpen,
onOpenChange,
...props
}) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
const isControlled = openProp !== undefined;
const currentOpen = isControlled ? openProp : uncontrolledOpen;
// 使用全局 hook 统一处理 body 滚动锁定 & 恢复,避免弹窗打开时页面跳到顶部
useBodyScrollLock(currentOpen);
const handleOpenChange = React.useCallback(
(next) => {
if (!isControlled) setUncontrolledOpen(next);
onOpenChange?.(next);
},
[isControlled, onOpenChange]
);
return (
<DialogPrimitive.Root
data-slot="dialog"
open={isControlled ? openProp : undefined}
defaultOpen={defaultOpen}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
function DialogTrigger({

View File

@@ -4,6 +4,7 @@ import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "@/lib/utils"
import { useBodyScrollLock } from "../../app/hooks/useBodyScrollLock"
const DrawerScrollLockContext = React.createContext(null)
@@ -12,36 +13,12 @@ const DrawerScrollLockContext = React.createContext(null)
* 既锁定滚动又保留视觉位置overlay 上 ontouchmove preventDefault 防止背景触摸滚动。
*/
function useScrollLock(open) {
const savedScrollYRef = React.useRef(0)
const onOverlayTouchMove = React.useCallback((e) => {
e.preventDefault()
}, [])
React.useEffect(() => {
if (!open || typeof document === "undefined") return
const scrollY = window.scrollY ?? window.pageYOffset
savedScrollYRef.current = scrollY
const prev = {
position: document.body.style.position,
top: document.body.style.top,
left: document.body.style.left,
right: document.body.style.right,
width: document.body.style.width,
}
document.body.style.position = "fixed"
document.body.style.top = `-${scrollY}px`
document.body.style.left = "0"
document.body.style.right = "0"
document.body.style.width = "100%"
return () => {
document.body.style.position = prev.position
document.body.style.top = prev.top
document.body.style.left = prev.left
document.body.style.right = prev.right
document.body.style.width = prev.width
window.scrollTo(0, savedScrollYRef.current)
}
}, [open])
// 统一使用 app 级 hook 处理 body 滚动锁定 & 恢复,避免多处实现导致位移/跳顶问题
useBodyScrollLock(open)
return React.useMemo(
() => (open ? { onTouchMove: onOverlayTouchMove } : null),