前提知识

常见大屏分辨率:

  • 1366 * 768 : 普通液晶显示器
  • 1920 * 1080: 高清液晶显示器
  • 2560 * 1440: 2K高清显示器
  • 4096 * 2160: 4K高清显示器
  • 1280 720: 笔记本(19201080分辨率下系统默认推荐150%缩放比产生的尺寸。。。。)

设备像素:

  • viewport: 视窗=浏览器窗口的宽高
  • 物理像素(设备像素): 屏幕显示分辨率像素,每个像素可以根据操作系统设置自己的颜色 和亮度
  • 设备独立像素(dip):密度无关像素,可以认为是计算机坐标系统中的一个点,可用于区分视网膜设备还是非视网膜设备
  • css像素(DIPs): 主要用在浏览器上,一般情况下,css像素称为与设备无关的像素
  • 屏幕密度: 设备表面上存在的像素数量(PPI)
  • 设备像素比(dpr): 定义了物理像素和设备独立像素的对应关系,设备像素比=物理像素/设备独立像素

    适配痛点:

  1. 设计稿按照1920*1080的分辨率,16:9的比例设计, 实际开发中,document窗口不足16:9(高度减掉顶部tab及导航栏,地址栏等)
  2. 不同分辨率:实际应用场景中,显示场景不同,不能固定写死px单位
  3. 不同比例:不同的显示器宽高比与设计稿不一致
  4. 由于大屏数据可视化项目通常用于放在电视或广告屏上展示用,而不允许出现滚动条

    关于rem

    rem (font size of the root element), 是 css3 的引入的一个大小单位。即相对于根元素的 font-size 值的大小。所谓根元素在网页里一般就是 html.
    1. html {
    2. font-size: 20px;
    3. }
    4. xx {
    5. width: 1.4rem; // 1.4 * 20px = 28px
    6. }
    通常如果应用场景只涉及16:9比例下,1920*1080分辨率屏幕的话,使用rem来自动计算即可实现不同大小屏幕的适配。

    关于px2rem

    在采用rem作为适配方案时,避免不了将设计稿中的px单位转化为rem单位,开发过程中如果每个值都手动计算免不了麻烦,通常作法:

    • 使用scss或less 函数进行计算
    • webpack结合postcss-px2rem自动计算

postcss 一种对css编译的工具,类似babel对js的处理,通过它的插件生态来实现各种功能转换,如:autoprefixer(. 自动补全浏览器前缀), px2rem(自动将px转换为rem)
这里对px2rem配置说明如下:
postcss-plugin-px2rem: 作为postcss的经典插件之一,用于自动转换px为rem postcss-px2rem文档

  1. 安装依赖:npm i postcss-plugin-px2rem -D
  2. 配置vue.config.js:
    1. css: {
    2. loaderOptions: {
    3. postcss: {
    4. plugins: [
    5. require('postcss-plugin-px2rem')({
    6. rootValue: 100, //换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。
    7. unitPrecision: 5, //允许REM单位增长到的十进制数字。
    8. propWhiteList: [], // 默认值是一个空数组,这意味着禁用白名单并启用所有属性。
    9. propBlackList: [], // 黑名单
    10. exclude: /(node_module)/, // 默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如node_module,如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
    11. selectorBlackList: [], // 要忽略并保留为px的选择器
    12. ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
    13. replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
    14. mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
    15. minPixelValue: 3 //设置要替换的最小像素值3px会被转rem,默认 0
    16. })
    17. ]
    18. }
    19. }
    20. }

    适配方案

    rem自适应缩放

    问题1:比例不是16:9的问题?

    当屏幕比16:9 宽时,两侧留白 当屏幕比16:9 高时,上下留白【这种少见】

通常做法:

我们将pc宽度 screenWidth 分为10等份,其中1等分的值作为html的font-size值,以1920*1080分辨率为基准设置px2rem的rootValue则为192。这种方式有如下2个特点: 所有长度的比例必然和设计图一致。 实际的显示长度完全由 html 的 font-size 值决定(线性关系)

问题2:以上的做法,在16:9的内容窗口中可以做到适配,但当窗口不是16:9时就会出现滚动条,于是我们针对问题1的做法则是当非16:9时,根据超出比例的那一边,对rem对应fontSize进行缩放
1.1 具体如下:

  1. 设:设计稿上有任一1条线: A, A 的长度为 x ,计算 rem 值的基准为z,那么 css 里,A 的长度表示为:大屏数据可视化——屏幕适配方案(多分辨率下) - 图1
  2. 设: 网页在不同分辨率下运行时html的font-size值为:大屏数据可视化——屏幕适配方案(多分辨率下) - 图2
  3. 那么 A 的实际显示长度就分为:大屏数据可视化——屏幕适配方案(多分辨率下) - 图3
    对于任意一条线,其x, z是固定的值,因而其长度根据页面运行时网页html的font-size大小而线性变化, 如在1920的设计宽度下,长100px的线,设计算rem的基准值是192(宽除以10),那么在1366分辨率下html的root-size为136.6, 则100px的线实际长度为100*136.6 / 192 (px)

1.2 进一步:

  1. 取计算rem值的基准是设计稿宽度的 1/q,假设设计稿宽度为ax, 高度为ay, 则计算rem的基准值z为:大屏数据可视化——屏幕适配方案(多分辨率下) - 图4
  2. 按上面公式可以得出浏览器中画布实际的宽,高分别为:
    宽 度:大屏数据可视化——屏幕适配方案(多分辨率下) - 图5
    高度:大屏数据可视化——屏幕适配方案(多分辨率下) - 图6
  3. 浏览器窗口的宽度 w 要等于画布实际的宽度,即
    大屏数据可视化——屏幕适配方案(多分辨率下) - 图7

1.3 再进一步: 不同宽高比下如何设置Fs值?

  1. 以常见的在浏览器窗口中举例,浏览器中画布宽度ax与设计稿一致,而高度比设计高度小,这时我们需要将高度调整为浏览器高度,而又严格按照设计稿的宽高比来进行,那么
    设浏览器document高度为h , 设缩小比例为S, 则根据1.2中2的公式得出,
    大屏数据可视化——屏幕适配方案(多分辨率下) - 图8即,大屏数据可视化——屏幕适配方案(多分辨率下) - 图9
  2. 由1.3中3的公式中得出Fs的值,代入以上公式可知
    大屏数据可视化——屏幕适配方案(多分辨率下) - 图10即:大屏数据可视化——屏幕适配方案(多分辨率下) - 图11

综上:假设设计稿为19201080其计算rem的基准值为192px (默认取宽度10等分),浏览器实际窗口为1920 937时rem的基准值则为 192S,即最终html的fontSize值为166.57px。
*最终方案:

  1. ;(function(designWidth, minWidth) {
  2. let docEle = document.documentElement;
  3. let screenRatioByDesign = 16 / 9;
  4. function setHtmlFontSize() {
  5. let screenRatio = docEle.clientWidth / docEle.clientHeight;
  6. let fontSize = (
  7. screenRatio > screenRatioByDesign ? (screenRatioByDesign / screenRatio) : 1
  8. ) * docEle.clientWidth / 10
  9. docEle.style.fontSize = fontSize.toFixed(3) + 'px';
  10. }
  11. setHtmlFontSize()
  12. window.addEventListener('resize', setHtmlFontSize)
  13. })(1920, 1024)
  1. module.exports = {
  2. // ...
  3. css: {
  4. loaderOptions: {
  5. postcss: {
  6. plugins: [
  7. require('postcss-plugin-px2rem')({
  8. rootValue: 192, //换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。
  9. unitPrecision: 5, //允许REM单位增长到的十进制数字。
  10. // propWhiteList: [], // 默认值是一个空数组,这意味着禁用白名单并启用所有属性。
  11. // propBlackList: [], // 黑名单
  12. exclude: /(node_module)/, // 默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如node_module,如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
  13. // selectorBlackList: [], // 要忽略并保留为px的选择器
  14. // ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
  15. replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
  16. mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
  17. minPixelValue: 3 //设置要替换的最小像素值3px会被转rem,默认 0
  18. })
  19. ]
  20. }
  21. }
  22. }
  23. }

CSS3 缩放scale

rem的方案对于1920及以上分辨率屏幕来说基本适用,但当切换到1366*768等小分辨率时,由于浏览器默认最小字体为12px,所以会导致文字比理想效果更多大, 而echarts生成的canvas图中单位是以固定px写死的,也会出现超出画布的问题,因此衍生第二种方案: scale缩放

思路:浏览器body设置为设计稿宽高即1920*1080, 动态根据实际宽高对body的width,height进行缩放,从而实现内容缩放

  1. body固定宽高:body{ width: 1920px; height: 1080px; }
  2. 动态获取实际文档宽高,并设置 body缩放系数

    1. ;(function(win) {
    2. let bodyStyle = document.createElement('style');
    3. bodyStyle.innerHTML = `body{width:1920px; height:1080px!important;}`
    4. document.documentElement.firstElementChild.appendChild(bodyStyle)
    5. function refreshScale(){
    6. let docWidth = document.documentElement.clientWidth
    7. let docHeight = document.documentElement.clientHeight
    8. let designWidth = 1920,
    9. designHeight = 1080,
    10. widthRatio = docWidth / designWidth,
    11. heightRatio = docHeight / designHeight;
    12. document.body.style = `transform:scale(${widthRatio},${heightRatio});transform-origin: left top;`;
    13. // 应对浏览器全屏切换前后窗口因短暂滚动条问题出现未占满情况
    14. setTimeout(function() {
    15. let lateWidth = document.documentElement.clientWidth
    16. let lateHeight = document.documentElement.clientHeight
    17. if (lateWidth === docWidth) return
    18. widthRatio = lateWidth / designWidth
    19. heightRatio = lateHeight / designHeight
    20. document.body.style = `transform:scale(${widthRatio},${heightRatio});transform-origin: left top;`;
    21. }, 0)
    22. }
    23. refreshScale()
    24. win.addEventListener('pageshow', function(e){
    25. // 浏览器后退的时候重新计算
    26. if (e.persisted) {
    27. refreshScale()
    28. }
    29. }, false)
    30. window.addEventListener('resize', setHtmlFontSize, false)
    31. })(window)
  3. 经过如上设置后,在项目中直接使用设计稿中px单位进行开发即可,当然这也有一个弊端,在非设计稿比例(16:9)下,界面会被压缩,但它的优势在于会对body内所有元素进行缩放,从而不会让echarts图表超出画布。

    Demo 演示

    以下demo均在浏览器窗口下展示(即非16:9情况下绘制设计16:9图稿)

1366*768分辨率下大屏 rem适配方案

在1366*768下采用rem方案能够基本适配不出现内容超出的情况(内容少时)

1920*1080缩放150%的情况下rem方案

在1920*1080分辨率系统缩放到150%时,可以看到此时rem方案已经有缺陷,canvas内图表超出,界面字体未达到设计稿缩放效果

1920*1080分辨率150%缩放下scale方案

采用scale方案,字体不受浏览器最小字体限制,可以自由绽放到该分辨率下对应比例

最新方案

由于在非16:9的情况下,默认效果是保证比例的前提下,居中两边留白,这种一些需求方觉得不好,特别是在只出了16:9的设计稿情况下,需要适配到另一个公司64:27(分辨率7680*3240)这种大屏而且要求要铺满不变形。。。 新痛点:

  1. UI提出在非16:9的情况下,图形不能被压缩(扁了)
  2. 客户提出在64:27这种宽屏下,两边不能留白太空

解决方案:设置宽度为浏览器宽度(默认Vue项目)

  1. function refreshScale(){
  2. let baseWidth = document.documentElement.clientWidth
  3. let baseHeight = document.documentElement.clientHeight
  4. let appStyle = document.getElementById('app').style;
  5. let realRatio = baseWidth / baseHeight
  6. let designRatio = 16 / 9
  7. let scaleRate = baseWidth / 1920
  8. if (realRatio > designRatio) {
  9. scaleRate = baseHeight / 1080
  10. }
  11. appStyle = `width:${baseWidth / scaleRate}px;transform:scale(${scaleRate}) translateX(-50%);transform-origin: left top;`;
  12. }

原文链接