119 lines
4.9 KiB
TypeScript
119 lines
4.9 KiB
TypeScript
/**
|
||
* 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
|