该方法会根据 目标分辨率(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;