🚀打造一个响应式全屏自适应缩放组件 —— React + TypeScript 实践分享

127 阅读9分钟

在数据大屏、仪表盘等场景中,全屏自适应缩放的 UI 展示越来越常见。传统的 CSS 实现方式虽然简单,但在复杂布局下可能会出现比例失衡、缩放模糊等问题。本文将分享一个使用 React + TypeScript 编写的 ScaleScreen 组件,帮助你快速实现高质量的自动缩放布局。

✨功能概述

ScaleScreen 是一个高可配置、支持全屏和按比例缩放的容器组件,具备以下特性:

  • ✅ 支持宽高设置全屏缩放
  • ✅ 可配置自动缩放方向(x/y)
  • ✅ 使用 MutationObserver 实时监听 DOM 样式变化
  • ✅ 提供缩放防抖处理,提升性能
  • ✅ 自动控制 body 的滚动隐藏
  • ✅ 使用 useLayoutEffect 实现精确渲染

📦组件属性说明

interface ScaleScreenProps {
  width?: number | string;           // 目标宽度,默认1920
  height?: number | string;          // 目标高度,默认1080
  fullScreen?: boolean;              // 是否铺满全屏
  autoScale?: boolean | { x: boolean; y: boolean }; // 缩放方向控制
  delay?: number;                    // 缩放防抖延迟
  boxStyle?: React.CSSProperties;    // 最外层容器样式
  wrapperStyle?: React.CSSProperties;// 包裹内容的容器样式
  bodyOverflowHidden?: boolean;      // 是否禁用页面滚动
  children?: React.ReactNode;        // 子组件内容
}

🧠核心实现思路

1️⃣ 自动缩放计算

根据 body.clientWidthbody.clientHeight 与目标尺寸 width/height 计算缩放比例:

const widthScale = currentWidth / realWidth;
const heightScale = currentHeight / realHeight;
const scale = Math.min(widthScale, heightScale);

再通过 transform: scale(x, y) 实现缩放:

wrapperRef.current.style.transform = `scale(${scale}, ${scale})`;

并根据是否启用 autoScale.x/y 决定是否设置 margin 来进行居中偏移。

2️⃣ 响应式 resize 监听 + MutationObserver 监听样式变化

useEffect(() => {
  const handleResize = debounce(() => {
    initSize();
    updateSize();
    updateScale();
  }, delay);

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, [delay]);

此外通过 MutationObserver 监听 style 变化,避免用户手动修改 DOM 后造成错位:

const observer = new MutationObserver(() => {
  if (resizeHandlerRef.current) {
    resizeHandlerRef.current();
  }
});

3️⃣ useLayoutEffect 控制尺寸和缩放

在 dimensions 更新后,使用 useLayoutEffect 立刻更新尺寸和缩放,保证子组件渲染前布局已生效,避免闪动:

useLayoutEffect(() => {
  updateSize();
  updateScale();
}, [dimensions, fullScreen, autoScale]);

🎨实际渲染结构

最终结构是一个 .scale-screen-box 的全屏容器包裹 .screen-wrapper 的内容区:

<div className="scale-screen-box" style={styles.box}>
  <div className="screen-wrapper" style={styles.wrapper} ref={wrapperRef}>
    {children}
  </div>
</div>

🧩使用方式

<ScaleScreen width={1920} height={1080} fullScreen={false} autoScale={{ x: true, y: false }}>
  <MyBigScreenDashboard />
</ScaleScreen>

你可以自由组合 fullScreenautoScale 实现不同的自适应效果。

完整代码

import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';

/**
 * 自动缩放配置类型
 */
type AutoScaleType =
  | boolean
  | {
      x: boolean;
      y: boolean;
    };

/**
 * ScaleScreen组件属性接口
 */
interface ScaleScreenProps {
  width?: number | string; // 宽度
  height?: number | string; // 高度
  fullScreen?: boolean; // 是否全屏
  autoScale?: AutoScaleType; // 是否自动缩放
  delay?: number; // 缩放延迟
  boxStyle?: React.CSSProperties; // 容器样式
  wrapperStyle?: React.CSSProperties; // 外层样式
  bodyOverflowHidden?: boolean; // 是否隐藏body的overflow
  children?: React.ReactNode; // 子组件
}

/**
 * 尺寸状态接口
 */
interface DimensionsState {
  width: number; // 宽度
  height: number; // 高度
  originalWidth: number; // 原始宽度
  originalHeight: number; // 原始高度
}

/**
 * 防抖函数
 * @param fn - 需要防抖的函数
 * @param delay - 延迟时间(毫秒)
 * @returns 防抖后的函数
 */
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
  let timer: ReturnType<typeof setTimeout> | null = null;

  return function (this: any, ...args: Parameters<T>): void {
    if (timer) clearTimeout(timer);

    timer = setTimeout(
      () => {
        typeof fn === 'function' && fn.apply(this, args);
        timer = null;
      },
      delay > 0 ? delay : 100
    );
  };
}

/**
 * 可缩放屏幕组件
 * @param width - 宽度
 * @param height - 高度
 * @param fullScreen - 是否全屏
 * @param autoScale - 是否自动缩放
 * @param delay - 缩放延迟
 * @param boxStyle - 容器样式
 * @param wrapperStyle - 外层样式
 * @param bodyOverflowHidden - 是否隐藏body的overflow
 * @param children - 子组件
 */
const ScaleScreen: React.FC<ScaleScreenProps> = ({
  width = 1920,
  height = 1080,
  fullScreen = false,
  autoScale = true,
  delay = 500,
  boxStyle = {},
  wrapperStyle = {},
  bodyOverflowHidden = true,
  children
}) => {
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  const observerRef = useRef<MutationObserver | null>(null);
  const bodyOverflowRef = useRef<string>('');
  const resizeHandlerRef = useRef<(() => void) | null>(null);

  const [dimensions, setDimensions] = useState<DimensionsState>({
    width: typeof width === 'number' ? width : parseInt(width, 10),
    height: typeof height === 'number' ? height : parseInt(height, 10),
    originalWidth: 0,
    originalHeight: 0
  });

  const styles: {
    box: React.CSSProperties;
    wrapper: React.CSSProperties;
  } = {
    box: {
      overflow: 'hidden',
      backgroundSize: '100% 100%',
      backgroundColor: '#000',
      width: '100vw',
      height: '100vh',
      ...boxStyle
    },
    wrapper: {
      transitionProperty: 'all',
      transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
      transitionDuration: '500ms',
      position: 'relative',
      overflow: 'hidden',
      zIndex: 100,
      transformOrigin: 'left top',
      ...wrapperStyle
    }
  };

  /**
   * 初始化大屏容器宽高
   */
  const initSize = (): void => {
    if (!wrapperRef.current) return;

    // 获取画布尺寸
    const originalWidth = window.screen.width;
    const originalHeight = window.screen.height;

    setDimensions(prev => ({
      ...prev,
      originalWidth,
      originalHeight
    }));
  };

  /**
   * 更新大屏容器宽高
   */
  const updateSize = (): void => {
    if (!wrapperRef.current) return;

    wrapperRef.current.style.width = `${dimensions.width}px`;
    wrapperRef.current.style.height = `${dimensions.height}px`;
  };

  /**
   * 处理自动缩放
   * @param scale - 缩放比例
   */
  const handleAutoScale = (scale: number): void => {
    if (!autoScale || !wrapperRef.current) return;

    const domWidth = wrapperRef.current.clientWidth;
    const domHeight = wrapperRef.current.clientHeight;
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    wrapperRef.current.style.transform = `scale(${scale},${scale})`;

    let mx = Math.max((currentWidth - domWidth * scale) / 2, 0);
    let my = Math.max((currentHeight - domHeight * scale) / 2, 0);

    if (typeof autoScale === 'object') {
      !autoScale.x && (mx = 0);
      !autoScale.y && (my = 0);
    }

    wrapperRef.current.style.margin = `${my}px ${mx}px`;
  };

  /**
   * 更新缩放比例
   */
  const updateScale = (): void => {
    if (!wrapperRef.current) return;

    // 获取真实视口尺寸
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    // 获取大屏最终的宽高
    const realWidth = dimensions.width || dimensions.originalWidth;
    const realHeight = dimensions.height || dimensions.originalHeight;

    if (!realWidth || !realHeight) return;

    // 计算缩放比例
    const widthScale = currentWidth / +realWidth;
    const heightScale = currentHeight / +realHeight;

    // 若要铺满全屏,则按照各自比例缩放
    if (fullScreen) {
      wrapperRef.current.style.transform = `scale(${widthScale},${heightScale})`;
      return;
    }

    // 按照宽高最小比例进行缩放
    const scale = Math.min(widthScale, heightScale);
    handleAutoScale(scale);
  };

  /**
   * 初始化body样式
   */
  const initBodyStyle = (): void => {
    if (bodyOverflowHidden) {
      bodyOverflowRef.current = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
    }
  };

  /**
   * 重置body样式
   */
  const resetBodyStyle = (): void => {
    if (bodyOverflowHidden) {
      document.body.style.overflow = bodyOverflowRef.current;
    }
  };

  /**
   * 初始化MutationObserver
   * @returns MutationObserver实例
   */
  const initMutationObserver = (): MutationObserver | undefined => {
    if (!wrapperRef.current) return;

    if (observerRef.current) {
      observerRef.current.disconnect();
    }

    const observer = new MutationObserver(() => {
      if (resizeHandlerRef.current) {
        resizeHandlerRef.current();
      }
    });

    observer.observe(wrapperRef.current, {
      attributes: true,
      attributeFilter: ['style'],
      attributeOldValue: true
    });

    observerRef.current = observer;
    return observer;
  };

  // 初始化resize事件处理函数
  useEffect(() => {
    // 创建防抖的resize处理函数
    const handleResize = debounce(() => {
      initSize();
      updateSize();
      updateScale();
    }, delay);

    // 保存引用以便MutationObserver使用
    resizeHandlerRef.current = handleResize;

    // 添加resize事件监听
    window.addEventListener('resize', handleResize);

    // 组件卸载时清理
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [delay]);

  // 处理props变化
  useEffect(() => {
    setDimensions(prev => ({
      ...prev,
      width: typeof width === 'number' ? width : parseInt(width, 10),
      height: typeof height === 'number' ? height : parseInt(height, 10)
    }));
  }, [width, height]);

  // 组件挂载和卸载
  useEffect(() => {
    // 初始化body样式
    initBodyStyle();

    // 初始化尺寸
    initSize();

    // 初始化MutationObserver
    const observer = initMutationObserver();

    // 组件卸载时清理
    return () => {
      observer?.disconnect();
      resetBodyStyle();
    };
  }, []);

  // 当dimensions更新时更新尺寸和缩放
  useLayoutEffect(() => {
    updateSize();
    updateScale();
  }, [dimensions, fullScreen, autoScale]);

  return (
    <div className="scale-screen-box" style={styles.box}>
      <div className="screen-wrapper" style={styles.wrapper} ref={wrapperRef}>
        {children}
      </div>
    </div>
  );
};

export default ScaleScreen;
import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';

/**
 * 自动缩放配置类型
 */
type AutoScaleType =
  | boolean
  | {
      x: boolean;
      y: boolean;
    };

/**
 * ScaleScreen组件属性接口
 */
interface ScaleScreenProps {
  width?: number | string; // 宽度
  height?: number | string; // 高度
  fullScreen?: boolean; // 是否全屏
  autoScale?: AutoScaleType; // 是否自动缩放
  delay?: number; // 缩放延迟
  boxStyle?: React.CSSProperties; // 容器样式
  wrapperStyle?: React.CSSProperties; // 外层样式
  bodyOverflowHidden?: boolean; // 是否隐藏body的overflow
  children?: React.ReactNode; // 子组件
}

/**
 * 尺寸状态接口
 */
interface DimensionsState {
  width: number; // 宽度
  height: number; // 高度
  originalWidth: number; // 原始宽度
  originalHeight: number; // 原始高度
}

/**
 * 防抖函数
 * @param fn - 需要防抖的函数
 * @param delay - 延迟时间(毫秒)
 * @returns 防抖后的函数
 */
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
  let timer: ReturnType<typeof setTimeout> | null = null;

  return function (this: any, ...args: Parameters<T>): void {
    if (timer) clearTimeout(timer);

    timer = setTimeout(
      () => {
        typeof fn === 'function' && fn.apply(this, args);
        timer = null;
      },
      delay > 0 ? delay : 100
    );
  };
}

/**
 * 可缩放屏幕组件
 * @param width - 宽度
 * @param height - 高度
 * @param fullScreen - 是否全屏
 * @param autoScale - 是否自动缩放
 * @param delay - 缩放延迟
 * @param boxStyle - 容器样式
 * @param wrapperStyle - 外层样式
 * @param bodyOverflowHidden - 是否隐藏body的overflow
 * @param children - 子组件
 */
const ScaleScreen: React.FC<ScaleScreenProps> = ({
  width = 1920,
  height = 1080,
  fullScreen = false,
  autoScale = true,
  delay = 500,
  boxStyle = {},
  wrapperStyle = {},
  bodyOverflowHidden = true,
  children
}) => {
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  const observerRef = useRef<MutationObserver | null>(null);
  const bodyOverflowRef = useRef<string>('');
  const resizeHandlerRef = useRef<(() => void) | null>(null);

  const [dimensions, setDimensions] = useState<DimensionsState>({
    width: typeof width === 'number' ? width : parseInt(width, 10),
    height: typeof height === 'number' ? height : parseInt(height, 10),
    originalWidth: 0,
    originalHeight: 0
  });

  const styles: {
    box: React.CSSProperties;
    wrapper: React.CSSProperties;
  } = {
    box: {
      overflow: 'hidden',
      backgroundSize: '100% 100%',
      backgroundColor: '#000',
      width: '100vw',
      height: '100vh',
      ...boxStyle
    },
    wrapper: {
      transitionProperty: 'all',
      transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
      transitionDuration: '500ms',
      position: 'relative',
      overflow: 'hidden',
      zIndex: 100,
      transformOrigin: 'left top',
      ...wrapperStyle
    }
  };

  /**
   * 初始化大屏容器宽高
   */
  const initSize = (): void => {
    if (!wrapperRef.current) return;

    // 获取画布尺寸
    const originalWidth = window.screen.width;
    const originalHeight = window.screen.height;

    setDimensions(prev => ({
      ...prev,
      originalWidth,
      originalHeight
    }));
  };

  /**
   * 更新大屏容器宽高
   */
  const updateSize = (): void => {
    if (!wrapperRef.current) return;

    wrapperRef.current.style.width = `${dimensions.width}px`;
    wrapperRef.current.style.height = `${dimensions.height}px`;
  };

  /**
   * 处理自动缩放
   * @param scale - 缩放比例
   */
  const handleAutoScale = (scale: number): void => {
    if (!autoScale || !wrapperRef.current) return;

    const domWidth = wrapperRef.current.clientWidth;
    const domHeight = wrapperRef.current.clientHeight;
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    wrapperRef.current.style.transform = `scale(${scale},${scale})`;

    let mx = Math.max((currentWidth - domWidth * scale) / 2, 0);
    let my = Math.max((currentHeight - domHeight * scale) / 2, 0);

    if (typeof autoScale === 'object') {
      !autoScale.x && (mx = 0);
      !autoScale.y && (my = 0);
    }

    wrapperRef.current.style.margin = `${my}px ${mx}px`;
  };

  /**
   * 更新缩放比例
   */
  const updateScale = (): void => {
    if (!wrapperRef.current) return;

    // 获取真实视口尺寸
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    // 获取大屏最终的宽高
    const realWidth = dimensions.width || dimensions.originalWidth;
    const realHeight = dimensions.height || dimensions.originalHeight;

    if (!realWidth || !realHeight) return;

    // 计算缩放比例
    const widthScale = currentWidth / +realWidth;
    const heightScale = currentHeight / +realHeight;

    // 若要铺满全屏,则按照各自比例缩放
    if (fullScreen) {
      wrapperRef.current.style.transform = `scale(${widthScale},${heightScale})`;
      return;
    }

    // 按照宽高最小比例进行缩放
    const scale = Math.min(widthScale, heightScale);
    handleAutoScale(scale);
  };

  /**
   * 初始化body样式
   */
  const initBodyStyle = (): void => {
    if (bodyOverflowHidden) {
      bodyOverflowRef.current = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
    }
  };

  /**
   * 重置body样式
   */
  const resetBodyStyle = (): void => {
    if (bodyOverflowHidden) {
      document.body.style.overflow = bodyOverflowRef.current;
    }
  };

  /**
   * 初始化MutationObserver
   * @returns MutationObserver实例
   */
  const initMutationObserver = (): MutationObserver | undefined => {
    if (!wrapperRef.current) return;

    if (observerRef.current) {
      observerRef.current.disconnect();
    }

    const observer = new MutationObserver(() => {
      if (resizeHandlerRef.current) {
        resizeHandlerRef.current();
      }
    });

    observer.observe(wrapperRef.current, {
      attributes: true,
      attributeFilter: ['style'],
      attributeOldValue: true
    });

    observerRef.current = observer;
    return observer;
  };

  // 初始化resize事件处理函数
  useEffect(() => {
    // 创建防抖的resize处理函数
    const handleResize = debounce(() => {
      initSize();
      updateSize();
      updateScale();
    }, delay);

    // 保存引用以便MutationObserver使用
    resizeHandlerRef.current = handleResize;

    // 添加resize事件监听
    window.addEventListener('resize', handleResize);

    // 组件卸载时清理
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [delay]);

  // 处理props变化
  useEffect(() => {
    setDimensions(prev => ({
      ...prev,
      width: typeof width === 'number' ? width : parseInt(width, 10),
      height: typeof height === 'number' ? height : parseInt(height, 10)
    }));
  }, [width, height]);

  // 组件挂载和卸载
  useEffect(() => {
    // 初始化body样式
    initBodyStyle();

    // 初始化尺寸
    initSize();

    // 初始化MutationObserver
    const observer = initMutationObserver();

    // 组件卸载时清理
    return () => {
      observer?.disconnect();
      resetBodyStyle();
    };
  }, []);

  // 当dimensions更新时更新尺寸和缩放
  useLayoutEffect(() => {
    updateSize();
    updateScale();
  }, [dimensions, fullScreen, autoScale]);

  return (
    <div className="scale-screen-box" style={styles.box}>
      <div className="screen-wrapper" style={styles.wrapper} ref={wrapperRef}>
        {children}
      </div>
    </div>
  );
};

export default ScaleScreen;

JS版

import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';

/**
 * 防抖函数
 * @param {Function} fn
 * @param {number} delay
 * @returns {() => void}
 */
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(
      () => {
        typeof fn === 'function' && fn.apply(null, args);
        clearTimeout(timer);
      },
      delay > 0 ? delay : 100
    );
  };
}

/**
 * 可缩放屏幕组件
 * @props {number} width - 宽度
 * @props {number} height - 高度
 * @props {boolean} fullScreen - 是否全屏
 * @props {boolean} autoScale - 是否自动缩放
 * @props {number} delay - 缩放延迟
 * @props {object} boxStyle - 容器样式
 * @props {object} wrapperStyle - 外层样式
 * @props {boolean} bodyOverflowHidden - 是否隐藏body的overflow
 * @props {children} - 子组件
 */
const ScaleScreen = ({
  width = 1920,
  height = 1080,
  fullScreen = false,
  autoScale = true,
  delay = 500,
  boxStyle = {},
  wrapperStyle = {},
  bodyOverflowHidden = true,
  children
}) => {
  const wrapperRef = useRef(null);
  const observerRef = useRef(null);
  const bodyOverflowRef = useRef('');
  const resizeHandlerRef = useRef(null);

  const [dimensions, setDimensions] = useState({
    width: typeof width === 'number' ? width : parseInt(width, 10),
    height: typeof height === 'number' ? height : parseInt(height, 10),
    originalWidth: 0,
    originalHeight: 0
  });

  const styles = {
    box: {
      overflow: 'hidden',
      backgroundSize: '100% 100%',
      backgroundColor: '#000',
      width: '100vw',
      height: '100vh',
      ...boxStyle
    },
    wrapper: {
      transitionProperty: 'all',
      transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
      transitionDuration: '500ms',
      position: 'relative',
      overflow: 'hidden',
      zIndex: 100,
      transformOrigin: 'left top',
      ...wrapperStyle
    }
  };

  /**
   * 初始化大屏容器宽高
   */
  const initSize = () => {
    if (!wrapperRef.current) return;

    // 获取画布尺寸
    const originalWidth = window.screen.width;
    const originalHeight = window.screen.height;

    setDimensions(prev => ({
      ...prev,
      originalWidth,
      originalHeight
    }));
  };

  /**
   * 更新大屏容器宽高
   */
  const updateSize = () => {
    if (!wrapperRef.current) return;

    wrapperRef.current.style.width = `${dimensions.width}px`;
    wrapperRef.current.style.height = `${dimensions.height}px`;
  };

  /**
   * 处理自动缩放
   */
  const handleAutoScale = scale => {
    if (!autoScale || !wrapperRef.current) return;

    const domWidth = wrapperRef.current.clientWidth;
    const domHeight = wrapperRef.current.clientHeight;
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    wrapperRef.current.style.transform = `scale(${scale},${scale})`;

    let mx = Math.max((currentWidth - domWidth * scale) / 2, 0);
    let my = Math.max((currentHeight - domHeight * scale) / 2, 0);

    if (typeof autoScale === 'object') {
      !autoScale.x && (mx = 0);
      !autoScale.y && (my = 0);
    }

    wrapperRef.current.style.margin = `${my}px ${mx}px`;
  };

  /**
   * 更新缩放比例
   */
  const updateScale = () => {
    if (!wrapperRef.current) return;

    // 获取真实视口尺寸
    const currentWidth = document.body.clientWidth;
    const currentHeight = document.body.clientHeight;

    // 获取大屏最终的宽高
    const realWidth = dimensions.width || dimensions.originalWidth;
    const realHeight = dimensions.height || dimensions.originalHeight;

    if (!realWidth || !realHeight) return;

    // 计算缩放比例
    const widthScale = currentWidth / +realWidth;
    const heightScale = currentHeight / +realHeight;

    // 若要铺满全屏,则按照各自比例缩放
    if (fullScreen) {
      wrapperRef.current.style.transform = `scale(${widthScale},${heightScale})`;
      return;
    }

    // 按照宽高最小比例进行缩放
    const scale = Math.min(widthScale, heightScale);
    handleAutoScale(scale);
  };

  // 初始化body样式
  const initBodyStyle = () => {
    if (bodyOverflowHidden) {
      bodyOverflowRef.current = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
    }
  };

  // 重置body样式
  const resetBodyStyle = () => {
    if (bodyOverflowHidden) {
      document.body.style.overflow = bodyOverflowRef.current;
    }
  };

  // 初始化MutationObserver
  const initMutationObserver = () => {
    if (!wrapperRef.current) return;

    if (observerRef.current) {
      observerRef.current.disconnect();
    }

    const observer = new MutationObserver(() => {
      if (resizeHandlerRef.current) {
        resizeHandlerRef.current();
      }
    });

    observer.observe(wrapperRef.current, {
      attributes: true,
      attributeFilter: ['style'],
      attributeOldValue: true
    });

    observerRef.current = observer;
    return observer;
  };

  // 初始化resize事件处理函数
  useEffect(() => {
    // 创建防抖的resize处理函数
    const handleResize = debounce(() => {
      initSize();
      updateSize();
      updateScale();
    }, delay);

    // 保存引用以便MutationObserver使用
    resizeHandlerRef.current = handleResize;

    // 添加resize事件监听
    window.addEventListener('resize', handleResize);

    // 组件卸载时清理
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [delay]);

  // 处理props变化
  useEffect(() => {
    setDimensions(prev => ({
      ...prev,
      width: typeof width === 'number' ? width : parseInt(width, 10),
      height: typeof height === 'number' ? height : parseInt(height, 10)
    }));
  }, [width, height]);

  // 组件挂载和卸载
  useEffect(() => {
    // 初始化body样式
    initBodyStyle();

    // 初始化尺寸
    initSize();

    // 初始化MutationObserver
    const observer = initMutationObserver();

    // 组件卸载时清理
    return () => {
      observer?.disconnect();
      resetBodyStyle();
    };
  }, []);

  // 当dimensions更新时更新尺寸和缩放
  useLayoutEffect(() => {
    updateSize();
    updateScale();
  }, [dimensions, fullScreen, autoScale]);

  return (
    <div className="scale-screen-box" style={styles.box}>
      <div className="screen-wrapper" style={styles.wrapper} ref={wrapperRef}>
        {children}
      </div>
    </div>
  );
};

export default ScaleScreen;