Skip to content

滚动

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,
  };
}