大屏适配解决方案

1,356 阅读7分钟

方法一: scale 缩放

背景

近期公司投放在展厅大屏中演示的大数据页面,出现了文字、图表、表格等多类组件显示错乱的情况。

目的

解决页面错乱问题,实现多种分辨率的大屏适配

入坑分析

俗话说的好,走过的最多路,就是网友们套路

1. vh、vw、rem组合方式

body,html {
  font-size:1vw
}
复制代码

描述:  定义父级大小,之后进行组件的height、margin、padding等多种css属性采用rem作为单位,继承父级设置等单位(1vw),实现适配。

坑点:  仅能满足一些特定的小场景,大屏中的实施非常不友好,例如我们覆盖element表格中的为font-size:0.5rem。此时投放到大屏,字体是自适应的,但其组件的多种默认属性用px为单位。如line-height的设置为22px,此时覆盖样式后的字体过大,并不能适应行高,就会出现文字重叠等错乱问题。

2. 采用font.js+rem的组合

(function(doc, win) {
  var docEl = doc.documentElement
  var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'
  var recalc = function() {
    var clientWidth = docEl.clientWidth
    if (!clientWidth) return
    docEl.style.fontSize = 100 * (clientWidth / 1920) + 'px'
  }
  if (!doc.addEventListener) return
  win.addEventListener(resizeEvt, recalc, false)
  doc.addEventListener('DOMContentLoaded', recalc, false)
})(document, window)
复制代码

描述:  引用第三方脚本,通过在main.js中写代码计算,使用rem进行继承,实现适配。

坑点:  原理跟上述方法1是一样的,同样无法实现大屏适配,坑点不在赘述。

解决思路

山重水复疑无路,柳暗花明又一村

当自己没有好的方法时,我们不妨借助下'巨人肩膀'的力量。本人找了下百度、网易等大数据适配的解决方案。通过线上案例发现如下所示规律(下方是我摘取的网易有数截图):

image.png

image.png

逻辑分析:

上图案例中采用了css3的缩放transform: scale(X)属性,对比两个图,我们不难发现本人改变分辨率时,scale的值是变化的。前端小伙们们看到这,想必也已经明白了大概。
我们只要监听浏览器窗口大的小,同时控制变化的比例就可以了。

1. 封装一个全局组件

以vue 为主,直接粘贴本人编写的代码如下,该公共组件为了下文中方便描述,命名为ScreenAdapter

<template>
  <div
    class="ScreenAdapter"
    :style="style"
  >
    <slot />
  </div>
</template>
<script>
export default {
  name: '',
  //参数注入
  props: {
    width: {
      type: String,
      default: '1920' 
    },
    height: {
      type: String,
      default: '1080' 
    }
  },
  data() {
    return {
      style: {
        width: this.width + 'px',
        height: this.height + 'px',
        transform: 'scale(1) translate(-50%, -50%)'
      }
    }
  },
  mounted() {
    this.setScale()
    window.onresize = this.Debounce(this.setScale, 1000)
  },
  methods: {
    Debounce: (fn, t) => {
      const delay = t || 500
      let timer
      return function() {
        const args = arguments
        if (timer) {
          clearTimeout(timer)
        }
        const context = this
        timer = setTimeout(() => {
          timer = null
          fn.apply(context, args)
        }, delay)
      }
    },
    // 获取放大缩小比例
    getScale() {
      const w = window.innerWidth / this.width
      const h = window.innerHeight / this.height
      return w < h ? w : h
    },
    // 设置比例
    setScale() {
      this.style.transform = 'scale(' + this.getScale() + ') translate(-50%, -50%)'
      console.log('任你千变万化,我都不会影响性能')
    }
  }
}
</script>
<style lang="scss" scoped>
.ScreenAdapter {
  transform-origin: 0 0;
  position: absolute;
  left: 50%;
  top: 50%;
  transition: 0.3s;
  background: red;
}
</style>
复制代码

2. 将此组件作为外壳,包在我们搭建的页面上

<ScreenAdapter> <div>大家好,我是大屏展示页面</div> </ScreenAdapter>

3. 定义网页规范

  • 根据美工给出的设计(主要获取美工给出的分辨率,如1920*1080)。
  • Div布局多采用flex+百分比布局(当然也可以根据美工给出的设计,默认写px。)。
  • 各类空间设计,根据美工给出的px进行定义即可

问题统一回复

问题:很多朋友说加入后会有留白的情况,需要调试。
解决:是因为注入的宽高,跟电脑实际分辨率宽高不一致的原因

问题:注入的宽高,跟分辨率宽高一致,为什么还有留白?
解决:

  1. 一致的话,只需要将浏览器全屏就可以看到正确的效果.

  2. 将注入的宽高换成document.documentElement.clientWidth、document.documentElement.clientHeight。即使不全屏也可以得到正确的结果
    这下大家应该明白了吧😊,基础很重要哦,哈哈~~~

方法二:大屏vw和vh适配方案

常见的屏幕分辨率

  • 1280 * 720: 老式电脑的屏幕,目前很少见到

  • 1366 * 768 : 普通液晶显示器

  • 1920 * 1080: 高清液晶显示器

  • 2560 * 1440: 2K高清显示器

  • 3840 * 2160: 4K高清显示器 自适应的难点

  • 要兼容不同屏幕尺寸的分辨率让页面在不同分辨率的屏幕下都保持正常的显示效果和比例

  • 不同分辨率,显示场景不同,不能固定写死px单位

  • 一些图表的自适应方式需要特别处理 关于vw和vh

  • 屏幕视口宽度 = 100vw

  • 屏幕视口高度 = 100vh

  • vw和vh也是css中标准的单位,和px,rem, %一样 vw和vh适配方案

  • 按照设计稿的尺寸,将px按比例计算转为vwvh

  • 转换公式如下 ` 假设设计稿尺寸为1920*1080(做之前一定问清楚UI设计稿的尺寸)

    即: 网页宽度=1920px 网页高度=1080px

    我们都知道 网页宽度=100vw 网页宽度=100vh

    所以,在1920x*1080px的屏幕分辨率下

    1920px = 100vw

    1080px = 100vh

    这样一来,以一个宽300px和200px的div来说,其作所占的宽高,以vw和vh为单位,计算方式如下:

    vwDiv = (300px / 1920px ) * 100vw vhDiv = (200px / 1080px ) * 100vh

    所以,就在1920*1080的屏幕分辨率下,计算出了单个div的宽高

    当屏幕放大或者缩小时,div还是以vw和vh作为宽高的,就会自动适应不同分辨率的屏幕 ` 但是每次都手动计算时不现实的,所以,我需要封装一个处理函数,让它帮我自动计算

这里我用到了scss

  • src/styles下新建一个utils.scss文件,定义好设计稿的宽度和高度两个变量
  • 在这里使用scss内置的math.div函数,定义两个vwvh的计算函数
  • 我们传入具体的像素值,其帮我们自动计算出vw和vh的值 utils.scss

我们的大屏的宽高比为4:3; `

//使用scss的math函数,https://sass-lang.com/documentation/breaking-changes/slash-div

@use "sass:math";

//默认设计稿的宽度
$designWidth:1600;
//默认设计稿的高度
$designHeight:1200;

//px转为vw的函数

@function vw($px) {
  @return math.div($px , $designWidth) * 100vw;
}

//px转为vh的函数

@function vh($px) {
  @return math.div($px , $designHeight) * 100vh;
}
复制代码

`

使用方式 依赖项

使用前安装对应版本的node包

  • "sass": "^1.26.5",
  • "sass-loader": "^8.0.2", 路径配置

我这里使用的是vue2.6和vue-cli3搭建的vue项目,所以,我只需要在vue.config.js里配置一下utils.scss的路径,就可以全局使用了

vue.config.js(只展示了与本方法有关的配置)

`

const path = require('path');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const defaultSettings = require('./src/settings.js');

function resolve(dir) {
  return path.join(__dirname, dir);
}

const name = defaultSettings.title; // page title
const port = process.env.port || process.env.npm_config_port || 8066; // dev port

const devUrl = process.env.DEV_URL; // dev url

module.exports = {
  publicPath: '/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  devServer: {
    port,
    open: true,
    overlay: {
      warnings: false,
      errors: true,
    },
    proxy: {
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        target: devUrl,
        changeOrigin: true,
        ws: false,
      },
    },
  },
  configureWebpack: {
    // provide the app's title in webpack's name field, so that
    // it can be accessed in index.html to inject the correct title.
    name,
    resolve: {
      alias: {
        '@': resolve('src'),
      },
    },
 
    performance: {
      maxEntrypointSize: 400000,
      maxAssetSize: 250000,
    },
  },
  css: {
    loaderOptions: {
    //全局配置utils.scss,详细配置参考vue-cli官网
      sass: {
        prependData: '@import "@/styles/utils.scss";',
      },
      less: {
        javascriptEnabled: true,
      },
    },
  },
};
复制代码

` 在.vue文件中使用

`

<script>
        export default{
                name: "Box",

        }
</script>

<style lang="scss" scoped="scoped">
 /* 直接使用vw和vh函数,将像素值传进去,得到的就是具体的vw vh单位*/
   .chart-wrapper{
     width: vw(400);
     height: vh(300);
     font-size: vh(16);
     background-color: black;
     margin: vw(20);
     border: vh(2) solid red;
   }

</style>
复制代码

`

特殊的使用情况

有的时候可能不仅在.vue文件中使用,比如在js中动态创建的DOM元素

它可能是直接渲染到html里面的

let oDiv = document.createElement('div')
document.body.appendChild(oDiv)
复制代码

这样的话,我用了以下两种处理方式,来给创建的div设置样式

1. 定义一些全局的class

新建一个global.scss文件,在main.js中引入

global.scss

    .global-div{
        width: vw(300);
        height: vw(200);
        background-color: green;
    }
    
复制代码

main.js

import './styles/global.scss'
复制代码

使用时,给创建的div设置className,

let oDiv = document.createElement('div')
oDiv.className = "global-div"
复制代码

2. 定义js样式处理函数

这种处理方式和scss处理函数类似,只不过使用了纯js将px转为vw和vh

新建一个styleUtil.js文件,内容如下

//定义设计稿的宽高
const designWidth = 1920;
const designHeight = 1080;
 
let styleUtil = {
    // px转vw
    px2vw: function (_px) {
        return _px * 100.0 / designWidth + 'vw';
    },
		// px转vh
    px2vh: function (_px) {
        return _px * 100.0 / designHeight + 'vh';
    },
 
};
 
export default styleUtil;
复制代码

使用时,单独设置宽高等属性

import styleUtil from "./src/utils/styleUtil.js"
 
let oDiv = document.createElement('div')
oDiv.style.width = styleUtil.px2vw(300)
oDiv.style.height = styleUtil.px2vh(200)
oDiv.style.margin = styleUtil.px2vh(20)
复制代码

不过这种使用方式有种弊端,就是屏幕尺寸发生变化后,需要手动刷新一下才能完成自适应调整