在驾驶舱、大屏可视化、数据看板项目中,屏幕适配一直是高频问题。
本文分享一个我在实际项目中使用的方案:
Vue3 + transform scale 缩放 + 防抖优化 + 组件封装
最终实现:
多分辨率自适应
页面始终完整显示
自动居中
缩放流畅不卡顿
可直接复用到多个驾驶舱项目
一、适配思路
设计稿尺寸固定:
1920 × 1080页面开发阶段完全按照设计稿编写。
运行时,根据当前浏览器窗口尺寸计算缩放比例:
当前宽度 / 1920
当前高度 / 1080取两者最小值:
scale = min(widthScale, heightScale)这样可以保证:
页面内容完整显示
不会裁切
保持原始比例
二、组件完整代码(ScaleBox.vue)
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'
const width = 1920
const height = 1080
const scaleBox = ref(null)
let resizeHandler = null
let resizeEndTimer = null
// 计算缩放比例
const getScale = () => {
const ww = window.innerWidth / width
const wh = window.innerHeight / height
return Number(Math.min(ww, wh).toFixed(4))
}
// 设置缩放
const setScale = () => {
const el = scaleBox.value
if (!el) return
const scale = getScale()
el.classList.add('no-transition')
el.style.setProperty('--scale', scale)
clearTimeout(resizeEndTimer)
resizeEndTimer = setTimeout(() => {
el.classList.remove('no-transition')
}, 180)
}
// 防抖函数
const debounce = (fn, delay = 100) => {
let timer = null
return () => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
}, delay)
}
}
onMounted(() => {
setScale()
resizeHandler = debounce(setScale, 50)
window.addEventListener('resize', resizeHandler)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeHandler)
})
</script>
<template>
<div
ref="scaleBox"
class="ScaleBox"
:style="{
width: `${width}px`,
height: `${height}px`
}"
>
<slot></slot>
</div>
</template>
<style>
.ScaleBox {
--scale: 1;
position: absolute;
top: 50%;
left: 50%;
z-index: 999;
display: flex;
flex-direction: column;
transform: scale(var(--scale)) translate(-50%, -50%);
transform-origin: 0 0;
transition: transform 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
will-change: transform;
}
.no-transition {
transition: none !important;
}
</style>三、核心代码解析
1. 为什么取最小值缩放?
Math.min(ww, wh)例如:
屏幕尺寸:
1600 × 900计算:
1600 / 1920 = 0.833
900 / 1080 = 0.833缩放比例:
0.833如果屏幕是超宽屏:
2560 × 1080计算:
2560 / 1920 = 1.333
1080 / 1080 = 1最终取:
1这样页面完整显示,只会左右留白,不会裁切内容。
2. 为什么居中?
top: 50%;
left: 50%;
transform: scale(...) translate(-50%, -50%);作用:
无论屏幕大小如何变化,页面始终居中显示。
非常适合驾驶舱场景。
3. 为什么 resize 要防抖?
拖动浏览器窗口时,会频繁触发:
resize如果每次都重新计算缩放,会造成:
卡顿
抖动
性能下降
所以加入防抖:
debounce(setScale, 50)50ms 内只执行一次。
4. 为什么临时关闭动画?
缩放变化过程中,如果保留 transition,会出现拖动窗口时动画抖动。
所以:
el.classList.add('no-transition')缩放结束后再恢复:
180ms 后移除这样体验更丝滑。
四、使用方式
在页面最外层包裹:
<ScaleBox>
<Dashboard />
</ScaleBox>然后内部所有内容仍按:
1920 × 1080开发即可,无需关心不同屏幕尺寸。值得注意的是,开发阶段最好使用 1920 × 1080 分辨率屏幕,或者至少使用 16:9 比例屏幕 进行开发与调试。
五、优点分析
完全按设计稿开发,无需频繁换算单位,并且封装成组件后,新项目直接复制即可使用。