滚动
01
vue
<template>
<div class="model-layout" @wheel="handleWheel" @touchmove="handleTouchMove">
<div class="mask">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
// 处理滚轮事件,允许下拉菜单滚动
function handleWheel(event: WheelEvent) {
// 检查是否在下拉菜单内部
const target = event.target as HTMLElement;
const dropdown = target.closest(".city-man-select-dropdown");
if (dropdown) {
// 如果在下拉菜单内部,允许滚动
return;
}
// 否则阻止滚动(防止背景滚动)
event.preventDefault();
}
// 处理触摸滚动事件,允许下拉菜单滚动
function handleTouchMove(event: TouchEvent) {
// 检查是否在下拉菜单内部
const target = event.target as HTMLElement;
const dropdown = target.closest(".city-man-select-dropdown");
if (dropdown) {
// 如果在下拉菜单内部,允许滚动
return;
}
// 否则阻止滚动(防止背景滚动)
event.preventDefault();
}
</script>
<style scoped lang="less">
.model-layout {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 100;
.mask {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px);
display: flex;
justify-content: center;
align-items: center;
}
}
</style>02
ts
import { onMounted, onUnmounted } from "vue";
/**
* 锁定/解锁 body 滚动的 composable
* 用于 Modal 组件防止背景滚动穿透
*/
export function useBodyScrollLock() {
let originalStyle = "";
let scrollBarWidth = 0;
const lock = () => {
// 保存原始样式
originalStyle = document.body.style.overflow;
// 计算滚动条宽度
scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
// 锁定滚动
document.body.style.overflow = "hidden";
// 防止布局偏移(滚动条消失导致的)
if (scrollBarWidth > 0) {
document.body.style.paddingRight = `${scrollBarWidth}px`;
}
};
const unlock = () => {
// 恢复原始样式
document.body.style.overflow = originalStyle;
document.body.style.paddingRight = "";
};
return {
lock,
unlock,
};
}
/**
* 自动管理 body 滚动锁定的 composable
* 组件挂载时锁定,卸载时解锁
*/
export function useAutoBodyScrollLock() {
const { lock, unlock } = useBodyScrollLock();
onMounted(() => {
lock();
});
onUnmounted(() => {
unlock();
});
return {
lock,
unlock,
};
}