react ant-design通用页面自适应适配不同分辨率屏幕的方法工具类

发布于:2025-06-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

该方法会根据 目标分辨率(options.width/height) 和 当前窗口尺寸(innerWidth/innerHeight) 计算缩放比例,并保持 等比例缩放Math.min(scaleX, scaleY)),确保内容不变形:

/**
 * 防抖函数
 * @param {Function} fn - 需要防抖的函数
 * @param {number} delay - 延迟时间(ms)
 * @returns {Function} 防抖后的函数
 */
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

/**
 * 带滚动条支持的自适应缩放
 * @param {string|HTMLElement} target - 目标元素
 * @param {Object} config - 配置项
 * @param {number} config.designWidth - 设计稿宽度(如1920)
 * @param {number} config.designHeight - 设计稿总高度(如1080)
 * @param {number} [config.topOffset=60] - 顶部固定高度(如导航栏)
 * @param {boolean} [config.scrollable=true] - 是否启用垂直滚动
 */
export function scrollableAutoScale(target, config) {
  const el = typeof target === 'string' ? document.querySelector(target) : target;
  if (!el) return () => { };

  const {
    designWidth = 1920,
    designHeight = 1080,
    topOffset1 = 0,
    topOffset2 = 0,
    scrollable = true
  } = config;

  // 1. 设备像素比检测和优化
  const dpr = window.devicePixelRatio || 1;
  const isRetina = dpr >= 1.5;
  const scalePrecision = isRetina ? 1000 : 400; // 视网膜屏使用更高精度

  // 2. 高清渲染样式优化
  Object.assign(el.style, {
    willChange: 'transform',
    backfaceVisibility: 'hidden',
    transformStyle: 'preserve-3d',
    textRendering: isRetina ? 'geometricPrecision' : 'optimizeLegibility',
    WebkitFontSmoothing: isRetina ? 'subpixel-antialiased' : 'antialiased',
    imageRendering: isRetina ? 'crisp-edges' : 'auto'
  });

  // 3. 创建外层容器(添加过渡效果、增加视网膜屏优化)
  const container = document.createElement('div');
  Object.assign(container.style, {
    position: 'fixed',
    top: `${topOffset2}px`,
    left: '0',
    width: '100vw',
    height: `calc(100vh - ${topOffset1 + topOffset2}px)`,
    overflow: 'hidden', // 默认隐藏,后面动态调整
    margin: '0',
    padding: '0',
    zIndex: '1',
    // 背景图优化
    backgroundImage: getComputedStyle(el).backgroundImage,
    backgroundPosition: getComputedStyle(el).backgroundPosition,
    backgroundRepeat: getComputedStyle(el).backgroundRepeat,
    backgroundSize: getComputedStyle(el).backgroundSize,
    imageRendering: isRetina ? 'crisp-edges' : 'auto',
    // 平滑过渡 滚动条出现/消失
    transition: 'overflow 0.2s ease-out'
  });

  el.style.backgroundImage = 'none';
  el.parentNode.insertBefore(container, el);
  container.appendChild(el);

  // 4. 目标元素样式(使用3D变换)
  Object.assign(el.style, {
    position: 'absolute',
    transformOrigin: 'top left',
    width: `${designWidth}px`,
    left: '0',
    right: '0',
    transformStyle: 'preserve-3d'
  });

  // 5. 增强版缩放计算
  let lastScale = 0;
  const calculate = () => {
    const viewportWidth = container.clientWidth;
    const viewportHeight = container.clientHeight;

    // 高精度缩放计算
    const rawScale = viewportWidth / designWidth;
    const scale = Math.round(rawScale * scalePrecision) / scalePrecision;

    const contentHeight = designHeight * scale;

    // 动态阈值(视网膜屏使用更敏感的阈值)
    const scaleThreshold = isRetina ? 0.001 : 0.0025;
    if (Math.abs(scale - lastScale) > scaleThreshold) {
      // 使用scale3d提升渲染质量
      el.style.transform = `scale3d(${scale}, ${scale}, 1)`;

      // 垂直居中优化
      if (!scrollable) {
        el.style.transform = `scale3d(${scale}, ${scale}, 1) translate3d(0, ${(viewportHeight - contentHeight) / 2}px, 0)`;
      }

      lastScale = scale;
    }

    // 滚动条缓冲(视网膜屏增加缓冲)
    const scrollBuffer = isRetina ? 5 : 2;
    container.style.overflowY = scrollable && (contentHeight > viewportHeight + scrollBuffer)
      ? 'auto'
      : 'hidden';
  };

  // 6. 高性能监听(双RAF防抖)使用requestAnimationFrame优化监听频率,避免高频触发计算导致性能问题。
  let rafId = null;
  const optimizedCalculate = () => {
    if (rafId) cancelAnimationFrame(rafId);
    rafId = requestAnimationFrame(() => {
      calculate();
      rafId = requestAnimationFrame(() => {
        calculate(); // 双RAF确保布局稳定
        rafId = null;
      });
    });
  };

  const observer = new ResizeObserver(debounce(optimizedCalculate, isRetina ? 30 : 50));
  observer.observe(container);

  // 7. 三重初始化保障
  requestAnimationFrame(() => {
    calculate();
    setTimeout(() => {
      requestAnimationFrame(calculate);
    }, 200);
  });

  return () => {
    observer.disconnect();
    if (rafId) cancelAnimationFrame(rafId);
    container.parentNode?.insertBefore(el, container);
    container.parentNode?.removeChild(container);
  };
}

react页面中的调用:

import React, { useEffect, useRef } from 'react';
import { optimizedAutoScale } from '../utils/optimizedAutoScale';

const Index = () => {
  const containerRef = useRef(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const destroy = scrollableAutoScale(containerRef.current, {
      designWidth: 1920,
      minHeight: 1080, // 设计稿总高度
      topOffset1: 53, // 动态获取顶部高度
      topOffset2: 42, // 动态获取顶部高度
      scrollable: true,    // 允许滚动
    });

    return () => {
      destroy();
    };
  }, []);

  return (
    <div ref={containerRef}>
      {/* 动态内容区域,高度会自动计算 */}
    </div>
  );
};

export default Index;

网站公告

今日签到

点亮在社区的每一天
去签到