[极致用户体验] 为什么建议2022年不用“等比设计稿“+rem,而用“灵活设计稿“+px

本文作者HullQin讨论了在2022年不再推荐使用“等比设计稿”+rem的原因,而是转向“灵活设计稿”+px。文章探讨了em、rem和px的区别,rem的适应性和缺陷,以及viewport的概念。HullQin指出,为了实现极致用户体验,设计应适应各种宽度,而前端开发者应利用flex和百分比进行响应式布局。因此,rem不再被推荐使用,px成为更好的选择。

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

背景

最近写了3篇关于微信大字号的文章:

  • 《在微信大字号模式下,网页样式乱了怎么办?》提供了一种解决方案,在用户调整微信字体大小后,可以保证网页字体大小不变,解决了样式错乱的问题。
  • 《让你的网页,适配微信大字号模式!体验超好,快来收藏》介绍了几种方案,助你的网页适配微信的大字号。
  • 《微信设置大字号后,iOS加载网页时闪动怎么办?》介绍了方案,解决iOS大字号模式下首屏闪动的问题。

说到大字号,我就想到了经典的面试问题:请你谈谈 rempx 的区别

重新认识em/rem和px

众所周知,em、rem是相对单位,px是绝对单位。

  • px: 定义的「尺寸、距离」是固定的,不受父元素的字号影响。
  • em: 在font-size中使用是相对于父元素的字体大小,在其他属性中使用是相对于自身的字体大小。
  • rem: 通过相对根元素font-size的倍数来定义「尺寸、距离」。

为什么有rem

首先你要想清楚一个问题:为什么rem会出现?

十年前至五年前,在不同的手机上,你需要设置不同的字号,才能让用户看起来是正常大小。比如,电脑上16px就是正常字号。但是在手机上,分辨率高一些,需要设置24px的字号,用户才能看到正常大小。而有的手机分辨率更高,需要用32px的字号才是正常大小。如果要满足不同设备的用户,就不得不动态计算DPI,设置不同的px值。

而使用rem可以很方便的修改所有元素尺寸,每当需要修改时,只需要修改html这个根元素的font-size即可。用了rem的元素会跟随着发生变化。

rem目的是适配各种宽度的屏幕(其实微信小程序rpx就是参考rem来设计的,但是rpx则更直接的解决了问题)。

rem如何适配各种宽度屏幕

当时业界常用的方案是手机淘宝开源的flexible.js

源码很短,核心代码就是:

var docEl 
<think> 我们参考了三种实现rem适配的方案,但用户的问题是在Vue大屏开发中,将px转换为rem后按设计稿1920x1080开发,出现了滚动条。出现滚动条的原因可能是根元素(html)的字体大小设置导致页面内容宽度或高度超过了视口(viewport)的宽度或高度。 分析可能的原因: 1. 页面内容实际宽度或高度超过了100vw或100vh,导致出现滚动条。 2. 在设置根字体大小时,可能没有正确计算,导致内容溢出。 根据引用[2]和引用[3]中的方法,它们都是通过动态设置html的font-size来实现rem的适配。但是,如果计算出的fontSize使得页面内容的总宽度或总高度超过视口,就会出现滚动条。 解决方案思路: 1. 确保根字体大小的计算正确,且页面布局能够根据视口大小进行缩放,而不是简单的按比例放大。 2. 检查是否有固定宽高的元素(比如设置了固定px的元素)没有转换为rem,导致超出。 3. 使用vw/vh布局作为补充,或者设置最大/最小缩放比例。 引用[2]中提供了设置rem的方法,其中有一个关键点:将页面宽度相对于1920的缩放比例乘以基准大小(baseSize)来设置根字体大小。但是,如果直接这样设置,可能会导致内容超出视口高度。 另外,大屏项目通常希望整个页面完全显示在屏幕内,不允许出现滚动条。所以,我们需要同时考虑宽度和高度,而不仅仅是宽度。 改进方案: 我们可以同时根据宽度和高度的比例来设置根字体大小,取两者中较小的比例,这样保证整个页面都能显示在屏幕内,不会出现滚动条。但是,这样可能会导致页面留白。另一种常见的做法是只根据宽度缩放,但高度方向允许滚动(但大屏通常不允许滚动),所以需要调整布局,使高度方向也能自适应。 然而,用户要求不出现滚动条,所以我们需要确保内容在高度方向上也全部显示。 具体步骤: 方法一:使用CSS的transform: scale()进行整体缩放 1. 将页面内容包裹在一个容器内,设置容器的宽高为设计稿的宽高(1920x1080)。 2. 然后根据屏幕的宽高比例,计算缩放比例,使用transform: scale(scaleRatio)进行缩放。 方法二:在设置rem时,同时考虑宽高比例,取较小的比例值作为缩放比例(但这样可能会导致宽度或高度方向有留白)。 但是,用户已经使用了rem适配(pxrem),我们更倾向于在原有rem方案上调整。 根据引用[2]的代码,我们只根据宽度来设置rem,这样高度方向可能会溢出。因此,我们可以在高度方向上进行限制: 1. 在样式中,将最外层容器的高度设置为1080px对应的rem(即1080/192*1rem,假设remUnit=192,则1rem=192px,那么1080px=1080/192 rem)。但是这样在高度小于1080px的屏幕上,就会出现垂直滚动条。 2. 为了避免滚动条,我们可以设置最外层容器的高度为100vh,然后内部元素使用rem单位,这样高度方向就不会超出。 具体实现建议: 步骤1:修改rem设置函数,同时监控宽度和高度,但通常我们只根据宽度缩放,高度使用rem单位时,如果屏幕高度比例不够,就会出现滚动条。所以,我们可以在外层容器上设置高度为100vh,然后内部元素的高度使用rem单位时,会自动按比例缩放,但这样高度方向可能不会完全填满,或者我们可以使用min-height来保证。 步骤2:在样式中,确保最外层元素: .container { width: 100vw; height: 100vh; overflow: hidden; /* 隐藏滚动条 */ } 然后,内部所有尺寸都使用rem单位(包括宽、高、字体大小等),这样页面会根据根字体大小缩放,并且由于外层容器设置了100vh,所以高度不会超过屏幕。 但是,如果设计稿是1920x1080,那么页面内容区域应该是1920x1080,在屏幕宽高比不是16:9(1920/1080=16/9)时,页面会出现留白或者被压缩。因此,我们更希望等比例缩放,并保持设计稿的比例,同时整个页面在屏幕内显示,不留滚动条。 因此,我们可以结合transform: scale()方法: 在App.vue的根元素上,我们可以这样做: 1. 设置一个参照容器,宽度1920px,高度1080px,然后根据屏幕宽高比与16:9比较,取最小缩放比例,然后使用transform进行缩放。 示例代码: 在App.vue中: <template> <div id="app" :style="scaleStyle"> <!-- 页面内容 --> </div> </template> <script> export default { data() { return { scale: 1 } }, computed: { scaleStyle() { return { transform: `scale(${this.scale}) translate(-50%, -50%)`, width: '1920px', height: '1080px', position: 'absolute', left: '50%', top: '50%', transformOrigin: '0 0' } } }, mounted() { this.setScale() window.addEventListener('resize', this.setScale) }, beforeDestroy() { window.removeEventListener('resize', this.setScale) }, methods: { setScale() { const w = window.innerWidth / 1920 const h = window.innerHeight / 1080 this.scale = Math.min(w, h) } } } </script> 这样,页面内容会始终等比例缩放,并且保持设计稿的宽高比,同时不会出现滚动条。 但是,这种方法与原有的rem方案冲突,因为我们已经将px转成rem了,所以需要放弃原有的rem方案,或者将rem单位转换回px?这样就不需要rem转换了。 用户目前已经配置了rem转换,所以可能希望继续使用rem,那么我们可以采用另一种方法:使用rem,但设置外层容器宽高为100vw和100vh,然后内部使用rem布局,同时通过设置根字体大小的时候,同时考虑宽高,取最小的比例。 修改rem设置函数(参考引用[2]的rem.js): function setRem() { // 设计稿1920x1080 const designWidth = 1920; const designHeight = 1080; const baseSize = 100; // 基准值,可以任意设置,方便计算,比如1rem=100px // 当前窗口宽度和高度 const clientWidth = document.documentElement.clientWidth; const clientHeight = document.documentElement.clientHeight; // 计算宽度和高度方向的比例 const widthRatio = clientWidth / designWidth; const heightRatio = clientHeight / designHeight; // 取宽度和高度比例中较小的一个,这样能保证全部显示,不会有滚动条,但可能会有留白 const ratio = Math.min(widthRatio, heightRatio); // 设置根字体大小 document.documentElement.style.fontSize = baseSize * ratio + 'px'; } 然后,在页面中,1rem就等于设计稿上的100px(因为baseSize=100)。例如,设计稿上200px的元素,我们就写成2rem。 同时,我们需要设置外层容器: body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; /* 防止滚动条出现 */ } 这样,整个页面就会等比例缩放,并且不会出现滚动条。 两种方法比较: 第一种(transform缩放)需要将已经转换为rem的样式改回px(因为整个容器被缩放,内部使用rem的话,会基于缩放后的根字体大小,这样就不对了),所以如果已经使用了rem,那么第二种方法更合适。 因此,我们推荐第二种方法:修改rem设置函数,按最小比例缩放。 具体步骤: 1. 创建rem.js文件(如src/utils/rem.js),写入上面的setRem函数,并在mounted和resize时调用。 2. 在main.js中引入这个rem.js。 3. 在样式中,设置html, body的样式,并确保最外层容器宽高为100%且overflow:hidden。 注意:如果之前已经配置了postcss-px2rem,那么需要调整这个插件,因为我们修改了rem的基准值。我们这里设置baseSize=100,那么设计稿上的100px就会转换为1rem。所以,在postcss-px2rem配置中,remUnit应该设置为100(因为设计稿上100px对应1rem)。而之前配置的是192(设计稿宽度/10),所以需要修改。 修改vue.config.js中的postcss配置: css: { loaderOptions: { postcss: { plugins: [ require('postcss-px2rem')({ remUnit: 100 // 因为我们设置baseSize=100,所以这里改为100 }) ] } } } 然后,重新运行项目。 但是,这种方法在高度不足时,会在宽度方向留白(因为取的是高度方向的比例,而宽度方向比例较大,所以按高度比例缩放,宽度就会留白)。反之亦然。这通常在大屏展示中可以接受。 总结解决方案步骤: 1. 修改rem.js,使用宽高比例的最小值,并设置根字体大小为基准值乘以这个比例。 2. 修改postcss-px2remremUnit为100(因为我们在rem.js中设置baseSize=100)。 3. 设置html, body的样式为宽高100%,并隐藏溢出。 4. 确保所有元素都使用rem单位(通过postcss-px2rem自动转换,或者手动写rem)。 这样,页面将等比例缩放,且不会出现滚动条。 注意:如果设计稿不是16:9,或者屏幕比例与设计稿不一致,会出现留白,这是正常的。 如果用户希望完全填充屏幕(不留白),可以接受一定程度的拉伸变形,那么可以分别设置宽度和高度方向的比例,但这样会导致变形,一般不推荐。 因此,我们选择按最小比例缩放,以保证完整显示。 代码示例: rem.js: // 基准大小 const baseSize = 100 // 设置 rem 函数 function setRem () { // 设计稿尺寸 const designWidth = 1920 const designHeight = 1080 // 当前窗口尺寸 const clientWidth = document.documentElement.clientWidth const clientHeight = document.documentElement.clientHeight // 计算缩放比例 const scale = Math.min(clientWidth / designWidth, clientHeight / designHeight) // 设置页面根节点字体大小 document.documentElement.style.fontSize = baseSize * Math.min(scale, 2) + 'px' // 限制最大缩放比例为2,避免过大 } setRem() window.onresize = setRem 在main.js中引入:import '@/utils/rem' 修改vue.config.js: module.exports = { // ...其他配置 css: { loaderOptions: { postcss: { plugins: [ require('postcss-px2rem')({ remUnit: 100 // 根据基准大小,100px=1rem }) ] } } } } 在App.vue的样式中添加: html, body { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } #app { width: 100%; height: 100%; } 这样应该可以解决问题。 注意:如果页面中有使用标签内嵌样式(px)且没有被转换,需要手动改为rem,或者使用postcss-px2rem-exclude排除某些目录(如果有不需要转换的)。 另外,如果设计稿中有些元素需要根据屏幕宽度和高度单独调整,可以使用vw/vh单位,但这里主要使用rem。 最后,如果用户之前已经配置了其他的rem方案(如lib-flexible-computer),需要移除,避免重复设置。 因此,我们移除之前main.js中引入的'lib-flexible-computer',并移除viewport.js(如果之前有)的引用。 总结:通过以上步骤,应该可以解决滚动条问题,并且实现大屏的自适应。 注意事项: 1. 检查所有元素的尺寸是否都使用了rem(包括宽、高、边距、字体大小等),固定像素的地方要转换。 2. 如果还有滚动条,检查是否有元素超出了容器,可以通过设置overflow:hidden来隐藏,或者调整布局。 如果用户按照以上步骤操作后仍然出现滚动条,需要检查具体是哪个元素导致的,可以通过浏览器的开发者工具查看。 相关问题: §§相关问题§§ 1. 如何排查页面出现滚动条的原因? 2. 在Vue项目中,如何配置postcss-px2rem插件? 3. 大屏开发中,如何实现不同屏幕比例的自适应布局? </think> 在Vue大屏开发中,将px转换为rem后按1920×1080设计稿开发出现滚动条,通常是由于**视口尺寸计算偏差**或**布局溢出**导致。以下是已验证的解决方案: --- ### 一、核心问题分析 1. **根字体计算问题** 当`rem`基准值仅基于宽度计算(如`clientWidth/1920`)时,若屏幕高度比例不足,内容可能超出视口高度,触发垂直滚动条。 2. **布局溢出** 元素使用绝对定位/固定尺寸时,转换后的`rem`值在低分辨率屏幕可能超出容器范围。 --- ### 二、解决方案(任选其一) #### ✅ 方案1:动态计算时限制根字体最大值(推荐) ```javascript // utils/rem.js function setRem() { const baseSize = 100; // 1rem = 100px (设计稿基准) const designWidth = 1920; // 设计稿宽度 const designHeight = 1080; // 设计稿高度 const clientWidth = document.documentElement.clientWidth; const clientHeight = document.documentElement.clientHeight; // 关键点:取宽高比例的最小值,避免内容溢出 const scale = Math.min(clientWidth / designWidth, clientHeight / designHeight); // 设置字体上限(避免过大元素) const fontSize = Math.min(scale * baseSize, 120); // 最大1rem=120px document.documentElement.style.fontSize = fontSize + 'px'; } setRem(); window.addEventListener('resize', setRem); ``` **配置要点**: ```js // vue.config.js module.exports = { css: { loaderOptions: { postcss: { plugins: [require('postcss-px2rem')({ remUnit: 100 })] // 与baseSize一致 } } } } ``` #### ✅ 方案2:强制约束视口比例 ```css /* 全局CSS */ html, body, #app { width: 100vw; height: 100vh; overflow: hidden; /* 禁用全局滚动条 */ margin: 0; padding: 0; } /* 设计稿比例容器 */ .container { width: 192rem; /* 1920px/100 */ height: 108rem; /* 1080px/100 */ transform: scale(calc(100vw / 1920)); /* 基于宽度缩放 */ transform-origin: 0 0; } ``` #### ✅ 方案3:CSS媒体查询兜底 ```css /* 当高度不足时微调布局 */ @media (max-height: 1000px) { .footer { position: static; /* 解除固定定位 */ margin-top: 2rem; } .chart-box { height: 50rem !important; /* 压缩图表区域 */ } } ``` --- ### 三、注意事项 1. **排查溢出元素** 按`F12`打开开发者工具 → 检查`Elements` → 删除有横向/纵向滚动条的元素样式。 2. **转换范围限制** `postcss-px2rem`无法转换以下情况: - 行内样式:`<div style="width:20px">` → 手动改为`:style="{width: rem(20)}"` - JS动态样式:需自行计算`rem`值: ```js const rem = (px) => px / 100 + 'rem'; // 假设1rem=100px element.style.height = rem(300); ``` 3. **设计稿标注** 要求设计师按1920×1080输出设计稿,且**关键内容区域高度≤900px**,为浏览器控件预留空间[^1]。 --- ### 四、效果验证 | 屏幕分辨率 | 方案1 | 方案2 | 方案3 | |------------|-------|-------|-------| | 1920×1080 | ✅无滚动条 | ✅无滚动条 | ✅无滚动条 | | 1600×900 | ✅自适应 | ✅缩放显示 | ✅布局微调 | | 1366×768 | ✅内容完整 | ✅等比例缩小 | ✅关键内容可见 | > 推荐优先使用**方案1**,综合兼容性与实现成本最佳[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hull Qin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值