一次搞懂数据大屏适配方案 (vw vh、rem、scale) 您所在的位置:网站首页 rem适配需要单独设置body字体大小 一次搞懂数据大屏适配方案 (vw vh、rem、scale)

一次搞懂数据大屏适配方案 (vw vh、rem、scale)

2024-03-15 09:41| 来源: 网络整理| 查看: 265

大厂技术  高级前端  Node进阶

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

前言

当接到可视化大屏需求时,你是否会有以下疑问👇如何做一款定制化的数据大屏? 开发可视化数据大屏如何做自适应? vm vh、rem、scale 到底哪种比较好? 时间不够,有没有偷懒的方法?

456d18e1d8aa6207ccc55adcd2959f2c.jpeg 最近在公司开发了一个可视化大屏,开发定制化大屏,大家可能都一个感受,开发大屏主要是两方面的工作:

大屏之关键-前期的自适应适配

根据 ui 稿绘制图表,调细节

而解决了适配问题后,后面就只是一个慢工出细活,耗时间的事情了。

适配方案分析

看了网上的各种方案,目前大家采用的大概有 3 种👇

方案实现方式优点缺点vm vh1.按照设计稿的尺寸,将px按比例计算转为vw和vh1.可以动态计算图表的宽高,字体等,灵活性较高 2.当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况1.每个图表都需要单独做字体、间距、位移的适配,比较麻烦scale1.通过 scale 属性,根据屏幕大小,对图表进行整体的等比缩放1.代码量少,适配简单 2.一次处理后不需要在各个图表中再去单独适配1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况  2.当缩放比例过大时候,字体会有一点点模糊,就一点点  3.当缩放比例过大时候,事件热区会偏移。rem + vm vh1.获得 rem 的基准值 2.动态的计算html根元素的font-size 3.图表中通过 vm vh 动态计算字体、间距、位移等1.布局的自适应代码量少,适配简单1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况 2.图表需要单个做字体、间距、位移的适配

以上 3 种方案在实际应用中该怎么选择视具体情况而定,也有看到大家说自适应在地图的适配中会有一些兼容问题,我这边还没有实践过。

如果想简单,客户能同意留白,选用 scale 即可

如果需要兼容不同比例的大屏,并且想在不同比例中都有比较好的效果,图表占满屏幕,类似于移动端的响应式,可以采用 vm vh 的方案

至于 rem,个人觉得就是 scale 和 vm vh 的综合,最终的效果跟 scale 差不多

接下来介绍下三种方案的具体实现,方案中的代码都以 vue2.0 和 vue-cli3 搭建的 vue 项目为例,因为是 demo,图表的一些细节就没有过多细致的调整了

方案一:vw vh 上效果 e122e30a4116a1b02ce8ce489d705268.jpeg ezgif.com-gif-maker (1).gif

当屏幕的尺寸比例刚好是 16:9 时

d011b004ead43186100584d360263d57.jpeg vwh02.jpg

当屏幕的尺寸比例大于 16:9 时

1109d22244c3cee7f443d5172e6d7f1b.jpeg vwh03.jpg

当屏幕的尺寸比例小于 16:9 时

f45d2ead30405b86cb8924369cd9fd4c.jpeg vwh09.jpg 实现思路

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

假设设计稿尺寸为 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 作为宽高的,就会自动适应不同分辨率的屏幕 复制代码 话不多说,上代码 css 方案 - 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里配置一下utils.scss的路径,就可以全局使用了

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";`,       },     },   }, }; 复制代码

在 .vue 中使用

             export default{     name: "Box", } /*   直接使用 vw 和 vh 函数,将像素值传进去,得到的就是具体的 vw vh 单位     */ .box{     width: vw(300);     height: vh(100);     font-size: vh(16);     background-color: black;     margin-left: vw(10);     margin-top: vh(10);     border: vh(2) solid red; } 复制代码 css 方案 - less

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; } 复制代码

路径配置在vue.config.js里配置一下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";`,       },     },   }, }; 复制代码

在 .vue 文件中使用

             export default{     name: "Box", } /*   直接使用 vw 和 vh 函数,将像素值传进去,得到的就是具体的 vw vh单位     */ .box{     .px2vw(width, 300);     .px2vh(height, 100);     .px2font(16);     .px2vw(margin-left, 300);     .px2vh(margin-top, 100);     background-color: black; } 复制代码 定义 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 项目中,也可以借助element-resize-detector,最好封装个 resize 的指令,在各图表中就只要使用该指令就可以了,毕竟作为程序员,能偷懒就偷懒

安装 element-resize-detector

npm install element-resize-detector --save

引入工具包在组件中使用或者在单独的 js 中使用

import resizeDetector from 'element-resize-detector'

封装 directive

// directive.js import * as ECharts from "echarts"; import elementResizeDetectorMaker from "element-resize-detector"; import Vue from "vue"; const HANDLER = "_vue_resize_handler"; function bind(el, binding) {   el[HANDLER] = binding.value     ? binding.value     : () => {         let chart = ECharts.getInstanceByDom(el);         if (!chart) {           return;         }         chart.resize();       };   // 监听绑定的div大小变化,更新 echarts 大小   elementResizeDetectorMaker().listenTo(el, el[HANDLER]); } function unbind(el) {   // window.removeEventListener("resize", el[HANDLER]);   elementResizeDetectorMaker().removeListener(el, el[HANDLER]);   delete el[HANDLER]; } // 自定义指令:v-chart-resize 示例:v-chart-resize="fn" Vue.directive("chart-resize", { bind, unbind }); 复制代码

main.js 中引入

import '@/directive/directive'; 复制代码

html 代码

           复制代码

这里要注意的是,图表中如果需要 tab 切换动态更新图表数据,在更新数据时一定不要用 echarts 的 dispose 方法先将图表移除,再重新绘制,因为 resize 指令中挂载到的图表实例还是旧的,就监听不到新的 chart 元素的 resize 了,更新数据只需要用 chart 的 setOption 方法重新设置配置项即可。

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

echarts 的字体大小只支持具体数值(像素),不能用百分比或者 vw 等尺寸,一般字体不会去做自适应,当宽高比跟 ui 稿比例出入太大时,会出现文字跟图表重叠的情况

3bf255b2c40f842c48b0db8491ce77b2.jpeg 这里我们就需要封装一个工具函数,来处理图表中文字自适应了👇

默认情况下,这里以你的设计稿是 1920*1080 为例,即网页宽度是 1920px (做之前一定问清楚 ui 设计稿的尺寸)

把这个函数写在一个单独的工具文件dataUtil.js里面,在需要的时候调用

其原理是计算出当前屏幕宽度和默认设计宽度的比值,将原始的尺寸乘以该值

另外,其它 echarts 的配置项,比如间距、定位、边距也可以用该函数

编写 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.fitChartFont = fitChartSize; 复制代码

这样你可以在.vue文件中直接使用this.fitChartSize()调用

   export default {   name: "dashboardChart",   data() {     return {       option: null,     };   },   mounted() {     this.getEchart();   },   methods: {     getEchart() {       let myChart = this.$echarts.init(this.$refs.chart);       const option = {         backgroundColor: "transparent",         tooltip: {           trigger: "item",           formatter: "{a} {b} : {c}%",         },         grid: {           left: this.fitChartSize(10),           right: this.fitChartSize(20),           top: this.fitChartSize(20),           bottom: this.fitChartSize(10),           containLabel: true,         },         calculable: true,         series: [           {             color: ["#0db1cdcc"],             name: "计划投入",             type: "funnel",             width: "45%",             height: "70%",             x: "5%",             minSize: "10%",             funnelAlign: "right",             center: ["50%", "50%"], // for pie             data: [               {                 value: 30,                 name: "下单30%",               },               {                 value: 55,                 name: "咨询55%",               },               {                 value: 65,                 name: "点击65%",               },               {                 value: 60,                 name: "访问62%",               },               {                 value: 80,                 name: "展现80%",               },             ].sort(function (a, b) {               return a.value - b.value;             }),             roseType: true,             label: {               normal: {                 formatter: function () {},                 position: "inside",               },             },             itemStyle: {               normal: {                 borderWidth: 0,                 shadowBlur: this.fitChartSize(20),                 shadowOffsetX: 0,                 shadowOffsetY: this.fitChartSize(5),                 shadowColor: "rgba(0, 0, 0, 0.3)",               },             },           },           {             color: ["#0C66FF"],             name: "实际投入",             type: "funnel",             width: "45%",             height: "70%",             x: "50%",             minSize: "10%",             funnelAlign: "left",             center: ["50%", "50%"], // for pie             data: [               {                 value: 35,                 name: "下单35%",               },               {                 value: 40,                 name: "咨询40%",               },               {                 value: 70,                 name: "访问70%",               },               {                 value: 90,                 name: "点击90%",               },               {                 value: 95,                 name: "展现95%",               },             ].sort(function (a, b) {               return a.value - b.value;             }),             roseType: true,             label: {               normal: {                 position: "inside",               },             },             itemStyle: {               normal: {                 borderWidth: 0,                 shadowBlur: this.fitChartSize(20),                 shadowOffsetX: 0,                 shadowOffsetY: this.fitChartSize(5),                 shadowColor: "rgba(0, 0, 0, 0.3)",               },             },           },         ],       };       myChart.setOption(option, true);     },   },   beforeDestroy() {}, }; .chartsdom {   width: 100%;   height: 100%; } 复制代码 方案二:scale

通过 css 的 scale 属性,根据屏幕大小,对图表进行整体的等比缩放,从而达到自适应效果

上效果 7fe40188bc97a89b1b0359de4e3ac307.jpeg sceen-scale.gif

当屏幕的尺寸比例刚好是 16:9 时,页面能刚好全屏展示,内容占满显示器

ea0fb7f44ebecc9ff26be9082f720cbb.jpeg sceen-demo.jpg

当屏幕的尺寸比例小于 16:9 时,页面上下留白,左右占满并上下居中,显示比例保持 16:9

9d26ec91c246444a3cf406d971ffca37.jpeg sceen-demo-02.jpg

当屏幕尺寸比例大于 16:9 时,页面左右留白,上下占满并居中,显示比例保持 16:9

8af3a61bd2e78410d59c9c25dcc23ccc.jpeg sceen-demo-03.jpg 话不多说,上代码

html 部分

            复制代码

js 部分

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%)`;   }, }, }; 复制代码

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  screenRatioByDesign         ? (screenRatioByDesign / screenRatio)         : 1     ) * docEle.clientWidth / 10;     docEle.style.fontSize = fontSize.toFixed(3) + "px";     console.log(docEle.style.fontSize);   }   setHtmlFontSize()   window.addEventListener('resize', setHtmlFontSize) })() 复制代码

在入口文件 main.js 中引入 rem.js 文件

import './utils/rem.js'; 复制代码

至此,页面就已经可以实现 16:9 自适应了。

第三点:屏幕变化,图表自适应屏幕变化后,图表自动调整字体、间距、位移等,此处参考上面 vm vh 的实现方式即可,在此就不重复赘述了

参考资料

推荐一个echarts 的案列网站,需要什么直接图表直接在上面去找,可以省去很多查 echarts 配置的时间全网echarts案例资源大总结和echarts的高效使用技巧(细节版)

scale 方案参考: 数据大屏最简单自适应方案,无需适配rem单位

vm vh 方案参考: Vue+Echarts企业级大屏项目适配方案

rem 方案参考:数据大屏rem适配方案

作者:懒惰的智慧 

链接:https://juejin.cn/post/7163932925955112996 

Node 社群

我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

e5f16f7d76c504d6f6b6afb4ae0614d5.jpeg

   “分享、点赞、在看” 支持一波👍



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有