vue3检测浏览器缩放hook

4 阅读1分钟
import { ref, Ref, onMounted, onUnmounted } from 'vue';

interface ZoomDetectorReturn {
  /** 当前缩放比例(百分比) */
  zoom: Ref<number>;
}

const ZOOM_KEYS = new Set(['+', '-', '=', '_']);
const ZOOM_KEY_CODES = new Set([61, 107, 173, 109, 187, 189]);

export function useDetectZoom(): ZoomDetectorReturn {
  const zoom = ref(100);

  const detectZoom = (): number => {
    if (typeof window === 'undefined') return 100;

    let ratio = 100;
    try {
      if (window.visualViewport?.scale) {
        ratio = window.visualViewport.scale * 100;
      } else if (window.devicePixelRatio) {
        ratio = window.devicePixelRatio * 100;
      } else if (window.outerWidth && window.innerWidth) {
        ratio = (window.outerWidth / window.innerWidth) * 100;
      }
    } catch (e) {
      console.warn('Zoom detection error:', e);
    }

    const rounded = Math.round(ratio);
    if (process.env.NODE_ENV !== 'production') {
      console.debug('Current zoom level:', `${rounded}%`);
    }
    return rounded;
  };

  const handleWheel = (e: WheelEvent) => {
    if (e.ctrlKey || e.metaKey) e.preventDefault();
  };

  const handleKeydown = (e: KeyboardEvent) => {
    if ((e.ctrlKey || e.metaKey) && (ZOOM_KEYS.has(e.key) || ZOOM_KEY_CODES.has(e.which))) {
      e.preventDefault();
    }
  };

  const updateZoom = () => (zoom.value = detectZoom());
  const debouncedUpdate = debounce(updateZoom, 100);

  onMounted(() => {
    if (typeof window === 'undefined') return;

    updateZoom();

    window.addEventListener('wheel', handleWheel, { passive: false });
    window.addEventListener('keydown', handleKeydown);
    window.addEventListener('resize', debouncedUpdate);

    if (window.visualViewport) {
      window.visualViewport.addEventListener('resize', debouncedUpdate);
    }
  });

  onUnmounted(() => {
    if (typeof window === 'undefined') return;

    window.removeEventListener('wheel', handleWheel);
    window.removeEventListener('keydown', handleKeydown);
    window.removeEventListener('resize', debouncedUpdate);

    if (window.visualViewport) {
      window.visualViewport.removeEventListener('resize', debouncedUpdate);
    }
  });

  return { zoom };
}

function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
  let timeoutId: ReturnType<typeof setTimeout>;
  return (...args: Parameters<T>) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}

使用

import { useDetectZoom } from '@/utils/useDetectZoom';

// 当前缩放
const { zoom } = useDetectZoom();