大屏适配方案

一、问题引入:

可视化数据大屏需要适配各种大屏尺寸 

1080P:1920*1080

2K:2560*1440 左右

4K:3840*2160 左右

8K:7680*4320 左右

二、适配方案分析

 

  • 如果想简单,客户能同意留白,选用 scale 即可
  • 如果需要兼容不同比例的大屏,并且想在不同比例中都有比较好的效果,图表占满屏幕,类似于移动端的响应式,可以采用 vm vh 的方案
  • 至于 rem,个人觉得就是 scale 和 vm vh 的综合,最终的效果跟 scale 差不多

三、方案一 vw+vh

实现思路

按照设计稿的尺寸,将px按比例计算转为vwvh,转换公式如下

假设设计稿尺寸为 1920*1080(做之前一定问清楚 ui 设计稿的尺寸)

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

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

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

1920px = 100vw

1080px = 100vh

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

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

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

当屏幕放大或者缩小时,div 还是以 vw 和 vh 作为宽高的,就会自动适应不同分辨率的屏幕

相关代码如下:

使用less方式:

1.安装插件:

npm i style-resources-loader --save-dev

2.新建utils.less文件:

@charset "utf-8";

// 默认设计稿的宽度
@designWidth: 1920;

// 默认设计稿的高度
@designHeight: 1080;

.px2vw(@name, @px) {
    @{name}: (@px / @designWidth) * 100vw;
}

.px2vh(@name, @px) {
    @{name}: (@px / @designHeight) * 100vh;
}

.px2font(@px) {
    font-size: (@px / @designWidth) * 100vw;
}

.px2vwCalc(@name, @px, @scale) {
    @{name}: calc(@scale - (@px / @designWidth) * 100vw);
}

.px2vhCalc(@name, @px, @scale) {
    @{name}: calc(@scale - (@px / @designHeight) * 100vh);
}
// padding margin简写
.px2pm(@name, @pxheight, @pxwidth) {
    @{name}: ((@pxheight / @designHeight) * 100vh)  ((@pxwidth / @designWidth) * 100vw);
}

3.路径配置
vue.config.js里配置一下utils.less

module.exports = {
    ...
    // 全局配置utils.less
    pluginOptions: {
     "style-resources-loader": { 
            preProcessor: "less", 
            patterns: [ path.resolve(__dirname, "./src/assets/css/utils.less") ] 
        }
  }
}

网上查资料亦可使用

const path = require("path");

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

module.exports = {
  publicPath: "",
  configureWebpack: {
    name: "app name",
    resolve: {
      alias: {
        "@": resolve("src"),
      },
    },
  },
  css: {
    // 全局配置utils.scss
    loaderOptions: {
      less: {
        additionalData: `@import "@/styles/utils.less";`,
      },
    },
  },
};

4.在vue文件中使用

.el-dialog__header {
            // padding: 24px;
            .px2vw(padding, 24);
            // padding-bottom: 20px;
            .px2vw(padding-bottom, 20);

            .el-dialog__headerbtn {
                // top: 20px;
                .px2vh(top, 20);
                // right: 20px;
                .px2vw(right, 20);
                // font-size: 16px;
                .px2font(16);
            }
        }

说明:如果报错 .px2vw 为undefined说明全局配置有问题,由于cli版本不同,安装下面插件即可解决

npm i vue-cli-plugin-style-resources-loader --save-dev

5.定义js样式处理函数 util.js

// 定义设计稿的宽高
const designWidth = 1920;
const designHeight = 1080;

// px转vw
export const px2vw = (_px) => {
    return (_px * 100.0) / designWidth + 'vw';
};

export const px2vh = (_px) => {
    return (_px * 100.0) / designHeight + 'vh';
};

export const px2font = (_px) => {
    return (_px * 100.0) / designWidth + 'vw';
};

全局写入vue原型

import { px2vw, px2vh, px2font } from './assets/utils'
Vue.prototype.$px2vw = px2vw
Vue.prototype.$px2vh = px2vh
Vue.prototype.$px2font = px2font

页面或者js中使用

<el-dialog :width="$px2vw(800)"></el-dialog>

6.图表字体、间距、位移等尺寸自适应

编写 dataUtil.js 工具函数

// Echarts图表字体、间距自适应
export const fitChartSize = (size,defalteWidth = 1920) => {
  let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
  if (!clientWidth) return size;
  let scale = (clientWidth / defalteWidth);
  return Number((size*scale).toFixed(3));
}

将函数挂载到原型上

import {fitChartSize} from '@src/utils/dataUtil.js'
Vue.prototype.fitChartSize = fitChartSize;

图表配置options中使用

legend: {
                    // top: 15,
                    top: _this.fitChartSize(15),
                    // itemWidth: 8,
                    itemWidth: _this.fitChartSize(8),
                    // itemHeight: 8,
                    itemHeight: _this.fitChartSize(8),
                    textStyle: {
                        fontFamily: 'AlibabaPuHuiTi-Regular',
                        // fontSize: 14,
                        fontSize: _this.fitChartSize(14),
                        color: '#FFFFFF',
                        fontWeight: 400
                    }
                },
                grid: {
                    // left: 80,
                    left: _this.fitChartSize(80),
                    // right: 170,
                    right: _this.fitChartSize(170),
                    // bottom: 30,
                    bottom: _this.fitChartSize(30),
                    // top: 50
                    top: _this.fitChartSize(50)
                },

说明:

1.页面尺寸改变图表自动调整,需要调用resize或者使用插件

2.此方法用于宽高比例正常16:9,4:3等等,若硬件为7680*2160 3840*1080这种超大宽屏,样式也会有问题,需单独写样式

超大宽屏适配处理:

尝试1:utils.less中使用媒体查询定义变量

@charset "utf-8";

// 默认设计稿的宽度
@designWidth: 1920;

// 默认设计稿的高度
@designHeight: 1080;

@media screen and (min-device-aspect-ratio:20/9) {
    // 默认设计稿的宽度
    @designWidth: 1920;

    // 默认设计稿的高度
    @designHeight: 1080;
}

这种写法并不能生效,less变量似乎不能在媒体查询中声明

 

尝试2:换种思路,新建一个宽屏下的css处理函数 utils_width.less,加大基础设计稿宽度尺寸,这样横向比例缩小,然后在页面中用媒体查询满足条件时使用宽屏css处理函数

@charset "utf-8";

// 默认设计稿的宽度
@designWidth_width: 3840;

// 默认设计稿的高度
@designHeight: 1080;

.px2vw_width(@name, @px) {
    @{name}: (@px / @designWidth_width) * 100vw;
}

.px2vh_width(@name, @px) {
    @{name}: (@px / @designHeight) * 100vh;
}

.px2font_width(@px) {
    font-size: (@px / @designWidth_width) * 100vw;
}

.px2vwCalc_width(@name, @px, @scale) {
    @{name}: calc(@scale - (@px / @designWidth_width) * 100vw);
}

.px2vhCalc_width(@name, @px, @scale) {
    @{name}: calc(@scale - (@px / @designHeight) * 100vh);
}

.px2pm_width(@name, @pxheight, @pxwidth) {
    @{name}: ((@pxheight / @designHeight) * 100vh) ((@pxwidth / @designWidth_width) * 100vw);
}

当然,需要在vue.config.js中导入两种全局less文件

// 全局配置utils.less
    pluginOptions: {
        "style-resources-loader": {
            preProcessor: "less",
            patterns: [
                path.resolve(__dirname, "./src/assets/css/utils.less"),
                path.resolve(__dirname, "./src/assets/css/utils_width.less")
            ]
        }
    }

同时,js处理函数和图表处理函数,根据屏幕宽高比调整适配比例

js处理函数

const clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
const clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const scale = clientWidth / clientHeight

// 定义设计稿的宽高
// 如果32/9宽屏,单独定义
const designWidth = scale > 16 / 9 ? 3840 : 1920;
const designHeight = 1080;

// px转vw
export const px2vw = (_px) => {
    return (_px * 100.0) / designWidth + 'vw';
};

export const px2vh = (_px) => {
    return (_px * 100.0) / designHeight + 'vh';
};

export const px2font = (_px) => {
    return (_px * 100.0) / designWidth + 'vw';
};

图表尺寸处理函数

// Echarts图表字体、间距自适应
export const fitChartSize = (size, defalteWidth = 1920) => {
    let clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    clientWidth / clientHeight > 16 / 9 && (defalteWidth = 3840);
    if (!clientWidth) return size;
    let scale = (clientWidth / defalteWidth);
    return Number((size * scale).toFixed(3));
}

 在页面中使用,使用媒体查询,满足宽屏时,使用宽屏css处理函数;而页面 js 设置的样式由于上述已经调整无需另外处理

@media screen and (min-device-aspect-ratio:20/9) {
        .contentBox{
            // width: 570px;
            .px2vw_width(width, 570);
        }
    }

此时即可适配超大宽屏

使用sass方式

util.scss

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

// 默认设计稿的宽度
$designWidth: 1920;
// 默认设计稿的高度
$designHeight: 1080;

// px 转为 vw 的函数
@function vw($px) {
  @return math.div($px, $designWidth) * 100vw;
}

// px 转为 vh 的函数
@function vh($px) {
  @return math.div($px, $designHeight) * 100vh;
}

路径配置

vue.config.js配置

const path = require("path");

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

module.exports = {
  publicPath: "",
  configureWebpack: {
    name: "app name",
    resolve: {
      alias: {
        "@": resolve("src"),
      },
    },
  },
  css: {
    // 全局配置 utils.scs,详细配置参考 vue-cli 官网
    loaderOptions: {
      sass: {
        prependData: `@import "@/styles/utils.scss";`,
      },
    },
  },
};

如若报错undefined,需要安装sass相关插件,具体自行查阅资料

四、方案二 scale

话不多说,上代码

html 部分

<div className="screen-wrapper">
    <div className="screen" id="screen">

    </div>
 </div>

js 部分

<script>
export default {
mounted() {
  // 初始化自适应  ----在刚显示的时候就开始适配一次
  handleScreenAuto();
  // 绑定自适应函数   ---防止浏览器栏变化后不再适配
  window.onresize = () => handleScreenAuto();
},
deleted() {
  window.onresize = null;
},
methods: {
  // 数据大屏自适应函数
  handleScreenAuto() {
    const designDraftWidth = 1920; //设计稿的宽度
    const designDraftHeight = 960; //设计稿的高度
    // 根据屏幕的变化适配的比例
    const scale =
      document.documentElement.clientWidth /
        document.documentElement.clientHeight <
      designDraftWidth / designDraftHeight
        ? document.documentElement.clientWidth / designDraftWidth
        : document.documentElement.clientHeight / designDraftHeight;
    // 缩放比例
    document.querySelector(
      '#screen',
    ).style.transform = `scale(${scale}) translate(-50%, -50%)`;
  },
},
};
</script>

css部分

/*
  除了设计稿的宽高是根据您自己的设计稿决定以外,其他复制粘贴就完事
*/  
.screen-root {
    height: 100%;
    width: 100%;
    .screen {
        display: inline-block;
        width: 1920px;  //设计稿的宽度
        height: 960px;  //设计稿的高度
        transform-origin: 0 0;
        position: absolute;
        left: 50%;
        top: -50%;
    }
}

实现思路

如何缩放

当屏幕宽高比 < 设计稿宽高比,我们需要缩放的比例是屏幕宽度 / 设计稿宽度
当屏幕宽高比 > 设计稿宽高比,我们需要缩放的比例是屏幕高度 / 设计稿高度

const scale = document.documentElement.clientWidth / document.documentElement.clientHeight < designDraftWidth / designDraftHeight ?
            (document.documentElement.clientWidth / designDraftWidth) :
            (document.documentElement.clientHeight / designDraftHeight);

如果我们拿到的设计稿宽高为: 1920 * 960 px ,而我们的屏幕大小是 1440 * 900 px,那么 1440/900 = 1.6,920/960 = 2

因为 1.6 < 2 (当前屏幕宽高比小于设计稿宽高比)

所以我们需要缩放的比例是:屏幕宽度除以设计稿宽度 = 1440/1920 = 0.75

如何居中
首先我们利用 transform:translate(-50%,-50%) ,将动画的基点设为左上角

transform-origin:设置动画的基点(中心点),默认点是元素的中心点

语法

transform-origin: x-axis y-axis z-axis;

然后利用transform:translate(-50%,-50%),将图表沿 x,y 轴移动 50%

image.png

接下来利用绝对定位将图表定位到中间位置

position: absolute;
left: 50%;
top: 50%;

五、方案三 rem + vw vh

思路如下,具体没有尝试

  1. 获得 rem 的基准值;
  2. 页面内写一段 js 代码,动态的计算html根元素的font-size
  3. 屏幕变化后,图表自动调整和图表字体、间距、位移等的自适应。

 

参考:https://juejin.cn/post/7163932925955112996#heading-20

 

 

posted @ 2022-12-07 17:09  盼星星盼太阳  阅读(4327)  评论(1编辑  收藏  举报