大屏适配方案
一、问题引入:
可视化数据大屏需要适配各种大屏尺寸
1080P:1920*1080
2K:2560*1440 左右
4K:3840*2160 左右
8K:7680*4320 左右
二、适配方案分析
- 如果想简单,客户能同意留白,选用
scale
即可 - 如果需要兼容不同比例的大屏,并且想在不同比例中都有比较好的效果,图表占满屏幕,类似于移动端的响应式,可以采用 vm vh 的方案
- 至于 rem,个人觉得就是 scale 和 vm vh 的综合,最终的效果跟
scale
差不多
三、方案一 vw+vh
实现思路
按照设计稿的尺寸,将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 作为宽高的,就会自动适应不同分辨率的屏幕
相关代码如下:
使用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%
接下来利用绝对定位
将图表定位到中间位置
position: absolute; left: 50%; top: 50%;
五、方案三 rem + vw vh
思路如下,具体没有尝试
- 获得 rem 的基准值;
- 页面内写一段 js 代码,动态的计算
html根元素的font-size
; - 屏幕变化后,图表自动调整和图表字体、间距、位移等的自适应。
参考:https://juejin.cn/post/7163932925955112996#heading-20