开发大屏主要是两方面的工作:
大屏之关键-前期的自适应适配
根据 ui 稿绘制图表,调细节
|-----------|----------------------------------|------------------------------------------------------|------------------------------|
| 方案 | 实现方式 | 优点 | 缺点 |
| vw vh | 1.按照设计稿的尺寸,将px
按比例计算转为vw
和vh
| 1.可以动态计算图表的宽高,字体等,灵活性较高 2.当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况 | 1.每个图表都需要单独做字体、间距、位移的适配,比较麻烦 |
实现思路
按照设计稿的尺寸,将px
按比例计算转为vw
和vh
,转换公式如下
Go
假设设计稿尺寸为 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
css
// 使用 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 中使用
javascript
<template>
<div class="box">
</div>
</template>
<script>
export default{
name: "Box",
}
</script>
<style lang="scss" scoped="scoped">
@import '@/assets/scss/util.scss';
/*
直接使用 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;
}
</style>
屏幕变化后,图表自动调整
这种使用方式有个弊端,就是屏幕尺寸发生变化后,需要手动刷新一下才能完成自适应调整
为了解决这个问题,你需要在各个图表中监听页面尺寸变化,重新调整图表,在 vue 项目中,最好封装个 resize 的指令,在各图表中就只要使用该指令就可以了。
-
封装 directive
javascript// 在directives目录下创建resizeObserver.js文件 // 监听元素大小变化的指令 const map = new WeakMap() const ob = new ResizeObserver((entries) => { for (const entry of entries) { // 获取dom元素的回调 const handler = map.get(entry.target) //存在回调函数 if (handler) { // 将监听的值给回调函数 handler({ width: entry.borderBoxSize[0].inlineSize, height: entry.borderBoxSize[0].blockSize }) } } }) export const Resize = { mounted(el, binding) { //将dom与回调的关系塞入map map.set(el, binding.value) //监听el元素的变化 ob.observe(el) }, unmounted(el) { //取消监听 ob.unobserve(el) } } export default Resize
-
在directives目录下创建index.js文件
javascriptimport Resize from "./resizeObserver"; // 监听dom宽高变化 const directivesList = { Resize }; const directives = { install: function (app) { Object.keys(directivesList).forEach((key) => { app.directive(key, directivesList[key]); // 注册 }); } }; export default directives;// 抛出
-
在vue中使用
javascript<!-- vue3 --> <template> <div class="content"> <div class="bar-content" id="bar-content" v-resize="onResize"> </div> <div class="bar-content" id="pie-content" v-resize="onResize"> </div> </div> </template> <script> const onResize = (width,height)=>{ nextTick(()=>{ myChart.resize() myPieChart.resize() myPieChart.clear();//消除当前实例 pieInit()//重新渲染echart // myPieChart.setOption(options,true);//重新渲染echart }) } onMounted(()=>{ barInit() pieInit() }) </script>
图表字体、间距、位移等尺寸自适应
echarts 的字体大小只支持具体数值(像素),不能用百分比或者 vw 等尺寸,一般字体不会去做自适应,当宽高比跟 ui 稿比例出入太大时,会出现文字跟图表重叠的情况
这里我们就需要封装一个工具函数,来处理图表中文字自适应了👇
-
默认情况下,这里以你的设计稿是 1920*1080 为例,即网页宽度是 1920px (做之前一定问清楚 ui 设计稿的尺寸)
-
把这个函数写在一个单独的工具文件
dataUtil.js
里面,在需要的时候调用 -
其原理是计算出当前屏幕宽度和默认设计宽度的比值,将原始的尺寸乘以该值
-
另外,其它 echarts 的配置项,比如间距、定位、边距也可以用该函数
-
编写 dataUtil.js 工具函数
javascript// 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)); }
-
在vue文件中import
javascriptimport { fitChartSize } from '@/assets/js/utils.js'
-
调用fitChartSize
javascript<template> <div class="chartsdom" ref="chart" v-chart-resize></div> </template> // 饼状图 let myPieChart = {} let options = {} const pieInit = ()=>{ // 基于准备好的dom,初始化echarts实例 myPieChart = echarts.init(document.getElementById('pie-content')); options = { // backgroundColor: 'rgb(43, 51, 59)', toolbox: { show: true, feature: { mark: { show: true }, dataView: { show: true, readOnly: false }, magicType: { show: true, type: ['pie', 'funnel'] }, restore: { show: true }, saveAsImage: { show: true } } }, calculable: true, "tooltip": { "trigger": "item", "formatter": "{a}<br/>{b}:{c}千万元" }, "title": { "text": "南丁格尔玫瑰图--PieHalfRose", "left": "center", "top": fitChartSize(20), "textStyle": { "color": "#ccc", "fontSize": fitChartSize(18) } }, "calculable": true, "legend": { "icon": "circle", "x": "center", "y": "15%", "data": [ "义乌市1", "义乌市2", "义乌市3", "义乌市4", "义乌市5", "义乌市6", "义乌市7", "义乌市8", "义乌市9" ], "textStyle": { "color": "#fff", "fontSize": fitChartSize(12) } }, "series": [{ "name": "XX线索", "type": "pie", "radius": [ fitChartSize(30), fitChartSize(70) ], "avoidLabelOverlap": false, "startAngle": 0, "center": [ "50%", "60%" ], "roseType": "area", "selectedMode": "single", "label": { "normal": { "show": true, "formatter": "{c}千万元", "color": '#fff', fontSize: fitChartSize(12) }, "emphasis": { "show": true } }, "labelLine": { "normal": { "show": true, "smooth": false, "length": fitChartSize(20), "length2": fitChartSize(10) }, "emphasis": { "show": true } }, "data": [{ "value": 600.58, "name": "义乌市1", "itemStyle": { "normal": { "color": "#f845f1" } } }, { "value": 1100.58, "name": "义乌市2", "itemStyle": { "normal": { "color": "#ad46f3" } } }, { "value": 1200.58, "name": "义乌市3", "itemStyle": { "normal": { "color": "#5045f6" } } }, { "value": 1300.58, "name": "义乌市4", "itemStyle": { "normal": { "color": "#4777f5" } } }, { "value": 1400.58, "name": "义乌市5", "itemStyle": { "normal": { "color": "#44aff0" } } }, { "value": 1500.58, "name": "义乌市6", "itemStyle": { "normal": { "color": "#45dbf7" } } }, { "value": 1500.58, "name": "义乌市7", "itemStyle": { "normal": { "color": "#f6d54a" } } }, { "value": 1600.58, "name": "义乌市8", "itemStyle": { "normal": { "color": "#f69846" } } }, { "value": 1800, "name": "义乌市9", "itemStyle": { "normal": { "color": "#ff4343" } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } }, { "value": 0, "name": "", "itemStyle": { "normal": { "label": { "show": false }, "labelLine": { "show": false } } } } ] }] } // 绘制图表 myPieChart.setOption(options); }