Skip to Content
全部文章万论前端大屏应用在不同分辨率下的适配方案

大屏应用在不同分辨率下的适配方案

前言

最近比较忙,好久没来写技术文章了,你说这年头忙点也是好事,说明公司还有钱赚,自然我们也能跟着喝汤,这两天才稍微缓过来一点,最近遇到一些在不同分辨率 下的 web 自适应问题,延伸下去今天想聊的就是,在不同分辨率下的 web 应用如何适配不同的屏幕尺寸,比较具有代表性的就是大屏应用,通常大屏的分辨率可能是 8K,甚至 16K。 我们如何保证应用在不同分辨率下的呈现效果一致就是一项挑战,今天跟大家讲下通常我会使用的几种方案以及他们的优劣。

方案一览

方案原理
transform scale 缩放方案通过在顶层元素上设置 scale 缩放级别来实现整个内部的子元素缩放
rem 方案不使用 px 而使用 rem 来当宽高单位,在不同分辨率计算修改根字体大小来达到缩放
viewport 方案不使用 px 而使用 vh 和 vw 来当宽高单位,实现比例式的缩放

Scale 方案

第一种方式通过 scale 来缩放元素,先说说实现方式,我最近有接触到一个应用,采用这种方案。 实现起来也比较简单,通过给一个 root 元素设置一个开发用的设计分辨率,然后监听窗口大小变化,根据窗口实际的大小去和你设定的设计分辨率进行计算比例。

比如我设置 1920x1080 的窗口,但是实际分辨率是 2k 显示器时,此时真实的分辨率是 2560x1440,通过计算得出:

💡
Tip

2560/1920=1.44 得到宽度的缩放比例

1440/1080=1.2 得到高度的缩放比例

此时去设置元素的 scaleX 和 scaleY 即可得到缩放

代码如下:

App.vue
<template> <div id="screen" :style="{ width: `${style.width}PX`, height: `${style.height}PX`, transform: `${style.transform}`, }" > <div id="app">你的实际应用内容</div> </div> </template> <script> export default { data() { return { timer: null, style: { width: '1920', // 设计设计稿宽度 height: '1080', // 设定设计稿高度 transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)', }, }; }, mounted() { let that = this; that.setScale(); // 窗口改变事件 window.onresize = () => { this.setScale(); }; }, methods: { // 获取宽高缩放比例 getScale() { const w = window.innerWidth / this.style.width; const h = window.innerHeight / this.style.height; return { x: w, y: h }; }, setScale() { // 处理页面的缩放 let scale = this.getScale(); this.style.transform = 'scaleY(' + scale.y + ') scaleX(' + scale.x + ') translate(-50%, -50%)'; // 处理一些ui框架挂载到body下,让这些挂载到body的元素也能缩放,这里用element来举例 const existingStyle = Array.from(document.querySelectorAll('style')).find( (style) => style.textContent.includes('.handled-el-dialog-transform') ); if (existingStyle) { existingStyle.remove(); } const style = document.createElement('style'); style.textContent = ` .handled-el-dialog-transform{} .el-dialog { transform: translate(-50%, -50%) ${ 'scaleY(' + scale.y + ') scaleX(' + scale.x + ')' } !important; } .el-popover { transform: ${ 'scaleY(' + scale.y + ') scaleX(' + scale.x + ')' } !important; } `; document.head.appendChild(style); }, }, }; </script>

来首先看看在 1080p 的分辨率下的显示效果,记住弹窗和文字的大小

1

然后来看看再 2k 分辨率下的显示效果,和上面 1080p 是几乎一样的,说明这套方案是可行的

1

Scale 方案的原理及优劣

首先 Scale 方案的缩放,它是拉伸或者压缩元素的,实际上你看到的所有元素的大小还是原来的大小,比如你弹窗宽度是 1800px,即便你在 4k 分辨率显示和 1080p 显示效果一致, 但在 4k 分辨率时,这个弹窗的宽度数值仍然是 1800px,而是他是通过渲染缩放来等比放大的,所以它会存在一些小问题,比如你见到的和实际的元素位置有偏移, 放大过多时会出现文字模糊,以及可能出现一些不能预期的情况例如滚动条的计算异常, 拿弹窗来说,可能出现在放大后出现滚动条,实际上你肉眼看到的窗口并不需要,这个滚动条的出现就是因为缩放导致的。如果你在 1080p 分辨率下开发,实际要运行在 8k、甚至 16k 的大屏上, 我就不是很建议使用 scale 缩放的方案了。

Rem 方案

Rem 作为一种和 px 一样的大小单位,它也可以用于做自适应分辨率的方案,em 是一种基于当前元素(如果没指定则基于父元素)的 font-size 的一种大小单位, 当有这样一个 div 时,这个 div 的宽度 1em 等价于 20px,高度 2em 等价于 40px。

<div style="font-size:20px;width:1em;heigth:2em"></div>

后来为了让 em 更好用,不用思考在某个元素上 em 等于多少,推出了 root em 单位:rem,他是基于根元素(body 元素)的的 font-size 来确定 1rem 具体是多少 px 的。

<body style="font-size:30px"> <div style="font-size:20px;width:1rem;heigth:2rem"></div> <div style="font-size:20px;width:1em;heigth:2em"></div> </body>

在上面的例子中,1rem 等于 30px,而 2rem 等于 60px。

基于 rem 这个可以根据 body 的 font-size 变化的特性,我们也能很容易得通过它来实现页面的自适应。

App.vue
<template> <div id="screen" :style="width:1920px;height:1080px"> <div id="app"> <div style="width:800px;height:300px">1</div> <div style="width:1800px;height:1300px">2</div> </div> </div> </template> <script> export default { data() { return {}; }, mounted() { let that = this; that.setScale(); // 窗口改变事件 window.onresize = () => { this.setScale(); }; }, methods: { // 获取宽高缩放比例 getScale() { const w = window.innerWidth / this.style.width; return w; }, setScale() { // 处理页面的缩放 let scale = this.getScale(); document.body.style.fontSize = `${scale * 16}px`; // 默认body的font-size是16px }, }, }; </script>

这样的情况下,页面的会随着屏幕自适应,但是有个致命的问题,那就是这个方案只能定义 1rem 等于多少,假如我是标准的 1080p 窗口拉伸到 2k 是没问题的,但是如果我从 1920*1080 的窗口, 我宽高不是等比的放大就会出问题,比如我放到一个带鱼屏上,宽了很多,但是高没有发生变化。此时竖向的尺寸计算都会乱套了,会出现高度和预期不一致,出现竖向滚动条等。

因此,我认为 Rem 方案并不适合用来做大屏应用(如果你不需要纵向自适应,则可以选用此方案),或者做分辨率自适应的场景。但是据我所知,是有人用这种方案实现自适应的。

ViewPort 视口方案

最后,我们来看看 Viewport 视口方案,这个方案比较好用,主流浏览器都支持,可以单独使用 viewport 来实现不同分辨率的自适应,也可以结合 scale 缩放方案一起。我们 来看看这两种方案有什么不一样的。

1.Scale 结合 Viewport 方案

这个方案比较适合要快速实现适配,改动非常小,或者不方便引用 postcss 包来构建应用的情况,这套方案比前面手动计算 scale 的方式要方便很多,性能也会好一些(忽略不计)。

你只需要再 body 上加一个样式,设置好页面的宽高+缩放公式

body { transform-origin: 0 0; transform: scaleX(calc(100vw / 1920)) scaleY(calc(100vh/1080)); width: 1920px; height: 1080px; overflow: auto; }

是不是非常的快速,你都不用改其他任何地方,当然因为使用了 scale,所以这套方案和上面的 Scale 方案的原理及优劣 说的问题一样,可能出现一些需要单独处理的样式问题。

2.viewport 方案

这个方案就是,你所有用 px 的地方都使用 vh 和 vw 来替代,当然如果这样的话,开发起来比较费事儿,老应用更是不可能去修改所有的 px 为 vh 和 vw 了,解决办法就是 需要借助一个 postcss 的插件来自动将系统中的所有写了 px 的样式,帮我们转换成 vh 和 vw。

首先安装 postcss 及它的插件

# 安装 PostCSS 核心及加载器 npm install postcss postcss-loader --save-dev # 安装 px-to-viewport 插件 npm install postcss-px-to-viewport --save-dev

项目根目录创建转换配置文件

postcss.config.js
module.exports = { plugins: { 'postcss-px-to-viewport': { unitToConvert: 'px', viewportWidth: 1920, // 设计稿宽度 viewportHeight: 1080, // 设计稿高度 unitPrecision: 5, // 支持对这些属性进行转换,支持通配符['*']转换所有px属性,也可以在这里指定转换哪些属性 propList: [ // 'width', // 'min-width', // 'max-width', // 'height', // 'min-height', // 'max-height', // 'top', // 'bottom', // 'left', // 'right' ], // 重点修改:使用函数动态指定视口单位 viewportUnit: (prop) => { if (prop.includes('height')) { return 'vh'; // 高度相关属性使用vh } return 'vw'; // 其他属性使用vw }, fontViewportUnit: 'vw', //字体使用vw还是vh selectorBlackList: [], minPixelValue: 1, mediaQuery: false, replace: true, exclude: /node_modules/i, landscape: false, }, }, };

这个插件支持的配置很详细,可以参考文档

在构建工具中使用 postcss 插件

// webpack.config.js 或 vue.config.js 等 module.exports = { module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader', // 确保 postcss-loader 在 css-loader 之后 ], }, ], }, };

项目构建之后你原来的样式:

.test { width: 100px; height: 50px; font-size: 16px; z-index: 10; }

构建完成会变成这个样子:

.test { width: 5.20833vw; height: 4.62963vh; font-size: 0.83333vw; z-index: 10; }

此时在任何分辨率都能保持一致的显示效果、比例了,这个方案是最现代化的方案,和上面 scale 的方式相比,我觉得这套方案是最优的选择,可以实现和 scale 一样的效果的同时, 还能保持住元素看到的和实际的位置都一致。

总结

最后,rem 方案是不建议选了,它的主要用途不是宽高适配,最后优先考虑纯 viewport+postcss 方案,配置麻烦点,但是维护性好。 不论是 px-to-viewport 插件,还是通过 scale 来实现缩放,总的来说,都是需要一开始预设好你的设计稿尺寸的。

最后编辑于

hi