/** * REM 适配插件 * 以 1920px 设计稿为基准,1rem = 100px * 小屏使用 transform scale 缩放,同时设置 CSS 变量 --vh 供页面使用真实视口高度 */ import type { Plugin } from 'vue' const remAdaptPlugin: Plugin = { install() { const docEl = document.documentElement const designWidth = 1920 // 检测是否为移动端/小屏 const isMobile = (): boolean => { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth < 1200 } // 重新计算缩放 const recalc = () => { const clientWidth = docEl.clientWidth if (!clientWidth) return // 始终使用 100px 作为基准 docEl.style.fontSize = '100px' const body = document.body if (!body) return if (isMobile()) { // 移动端首页(/m):不使用 scale 缩放,直接以移动端原生尺寸渲染 const isMobileHome = window.location.pathname === '/m' if (isMobileHome) { body.style.width = '' body.style.height = '' body.style.minHeight = '' body.style.overflowY = '' body.style.transform = '' body.style.transformOrigin = '' docEl.style.height = '' docEl.style.overflow = '' // 移动端首页 1rem = 视口宽度 / 3.90(设计稿 390px) const mobileFontSize = clientWidth / 3.90 docEl.style.fontSize = mobileFontSize + 'px' docEl.style.setProperty('--app-height', '100vh') docEl.style.setProperty('--chat-width', '100%') return } // 其他页面:使用 transform scale 缩放整个页面 const scale = clientWidth / designWidth body.style.width = designWidth + 'px' body.style.transform = `scale(${scale})` body.style.transformOrigin = 'top left' // 屏幕越小 scale 越小,适当增大 rem 基准让字体视觉上更大 // 首页和其他页面使用不同的缩放系数 const isHomePage = window.location.pathname === '/' || window.location.pathname === '/index.html' || window.location.pathname === '/m' const homeMultiplier = 0.6 // 首页字体放大系数,可单独调整 const otherMultiplier = 2.0 // 其他页面字体放大系数 const multiplier = isHomePage ? homeMultiplier : otherMultiplier const homeMaxFontSize = 180 // 首页最大 fontSize 上限 const otherMaxFontSize = 630 // 其他页面最大 fontSize 上限 const maxFontSize = isHomePage ? homeMaxFontSize : otherMaxFontSize const fontScale = 1 + (1 - scale) * multiplier const fontSize = Math.min(100 * fontScale, maxFontSize) docEl.style.fontSize = fontSize + 'px' // 动态计算聊天面板宽度:屏幕越窄,宽度从 4rem 缩到 1.0rem // 用 scale 直接驱动(scale=0.625 时满宽4rem,scale 越小宽度越小) const chatWidth = 1.0 + (4.0 - 1.0) * scale // scale=1→4rem, scale=0.5→2.5rem, scale=0.3→1.9rem docEl.style.setProperty('--chat-width', Math.max(chatWidth, 1.0).toFixed(2) + 'rem') // 小屏导航宽度CSS变量 docEl.style.setProperty('--nav-width', '0.8rem') // body 高度 = 视口高度 / scale,使其缩放后刚好等于视口高度 // overflow-y: auto 让内容在 body 内部滚动 const realViewHeight = window.innerHeight / scale body.style.height = realViewHeight + 'px' body.style.overflowY = 'auto' body.style.minHeight = '' // 设置 CSS 变量让页面组件可以使用真实视口高度(设计稿尺度下) docEl.style.setProperty('--app-height', realViewHeight + 'px') // html 固定视口高度 + overflow hidden,裁剪 body 缩放后的布局占位 docEl.style.height = '100vh' docEl.style.overflow = 'hidden' } else { // PC 端:移除缩放相关样式 body.style.width = '' body.style.height = '' body.style.minHeight = '' body.style.overflowY = '' body.style.transform = '' body.style.transformOrigin = '' docEl.style.height = '' docEl.style.overflow = '' docEl.style.setProperty('--app-height', '100vh') docEl.style.setProperty('--chat-width', '3.6rem') // 根据窗口宽度设置导航栏宽度CSS变量 const navWidth = window.innerWidth < 1440 ? '0.8rem' : '2.2rem' docEl.style.setProperty('--nav-width', navWidth) } } // 监听窗口变化 window.addEventListener('resize', recalc, false) window.addEventListener('orientationchange', recalc, false) // 初始执行,延迟确保 DOM 加载完成 if (document.readyState === 'complete') { recalc() } else { window.addEventListener('load', recalc) } } } export default remAdaptPlugin