ECharts搭建数据可视化大屏的多种可落地方案及对比

577 阅读6分钟

数据可视化之使用echarts搭建可视化大屏

什么是数据可视化?

数据可视化简单理解,就是将数据转换成易于人员辨识和理解的视觉表现形式,如各种 2D 图表、3D 图表、地图、矢量图等等,随着技术的不断进步,数据可视化的边界也在不断扩大

数据可视化的应用场景

  1. 数据大屏

    screen

  2. 数据报表

    report

  3. 地图

    map

数据可视化的解决方案

可视化解决方案

Skia

Skia 是 Chrome 和 Android 的底层 2D 绘图引擎,具体可参考百度百科,Skia 采用 C++ 编程,由于它位于浏览器的更底层,所以我们平常接触较少

OpenGL

OpenGL(Open Graphics Library)是2D、3D图形渲染库,它可以绘制从简单的2D图形到复杂的3D景象。OpenGL 常用于 CAD、VR、数据可视化和游戏等众多领域。

Chrome

Chrome 使用 Skia 作为绘图引擎,向上层开放了 canvas、svg、WebGL、HTML 等绘图能力。

对于前端来说,对于这些底层的不用了解太多,只需要关注上面红色区域的解决方案,目前使用最广泛的有ECharts、Highcharts等

ECharts VS Highcharts

Highcharts 和 ECharts 的争论非常多,整体来说,我个人的感受是:

  • Highcharts 能够兼容 IE6+,ECharts 通过 VML 兼容低端浏览器
  • Highcharts 文档体验略胜一筹
  • Highcharts 收费,这是很多开发者转向 ECharts 的主要原因
  • Highcharts 基于 svg 实现,ECharts 默认采用 canvas 渲染,4.0 支持 svg 渲染
  • ECharts 国内知名度更高,国内企业认可度更高

ECharts VS AntV

  • AntV 文档阅读体验更符合互联网产品使用习惯
  • AntV 产品体系拆分更加清晰,但一定程度上提升了学习成本
  • ECharts 社区更强大
  • ECharts 使用更加广泛

ECharts 优势总结

  • 简单易用
  • 文档全面
  • 社区强大
  • 高知名度

如何使用ECharts

实现一个最简单的柱状图

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script>
    <style>
      #chart {
        width: 800px;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="chart"></div>
    <script>
      const chartDom = document.getElementById('chart')
      const chart = echarts.init(chartDom)
      chart.setOption({
        title: {
          text: '快速入门ECharts开发'
        },
        xAxis: {
          data: ['食品', '数码', '服饰', '箱包']
        },
        yAxis: {},
        series: {
          type: 'bar',
          data: [100, 120, 90, 150]
        }
      })
    </script>
  </body>
</html>

ECharts适配方案

我查阅了很多的资料,收集了很多项目的大屏适配方案,主要来说,分为几种:

基于 flexible.js + rem 智能大屏适配

flexible.js是淘宝移动端自适应解决方案,源码含注解如下:

// 首先是一个立即执行函数,执行时传入的参数是window和document
(function flexible(window, document) {
  // 返回文档的root元素
  var docEl = document.documentElement; 
  // 获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
  var dpr = window.devicePixelRatio || 1; 

  // 设置默认字体大小,默认的字体大小继承自body
  function setBodyFontSize() {
    if (document.body) {
      // 调整body标签的fontSize,fontSize = (12 * dpr) + 'px'
      document.body.style.fontSize = 12 * dpr + 'px';
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize);
    }
  }
  setBodyFontSize();

  // set 1rem = viewWidth / 24
  function setRemUnit() {
    // 设置root元素的fontSize = 其clientWidth / 24 + 'px'
    var rem = docEl.clientWidth / 24;
    docEl.style.fontSize = rem + 'px';
  }
  setRemUnit();

  // 当页面展示或重新设置大小的时候,触发重新
  window.addEventListener('resize', setRemUnit);
  window.addEventListener('pageshow', function(e) {
    if (e.persisted) {
      setRemUnit();
    }
  });

  // 检测0.5px的支持,支持则root元素的class中有hairlines
  if (dpr >= 2) {
    var fakeBody = document.createElement('body');
    var testElement = document.createElement('div');
    testElement.style.border = '.5px solid transparent';
    fakeBody.appendChild(testElement);
    docEl.appendChild(fakeBody);
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines');
    }
    docEl.removeChild(fakeBody);
  }
})(window, document);

以设计稿1920px为例,将其等分成24等份,那么1rem = 80px

显而易见,通过flexible.js的方式,不仅要引入这个外部js文件,通过读源码,还得了解关于dpr、rem等相关知识,还需要考虑document的fontsize的大小,然后在写css的时候需要将px转化为rem,这样就会有很多踩坑的地方,会花费大量的时间

具体方案可以参考这位大佬的文章和开源项目:juejin.cn/post/684516…

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作为宽高的,就会自动适应不同分辨率的屏幕

所以,我们每次写css时都需要把px转化为vw、vh,所以我们需要借助scss的函数来帮我们计算。或者是通过postcss-px-to-viewport包去转换,下面采用第一种方案:

首先我们得安装scss

npm install sass@1.26.5 sass-loader@8.0.2  --save  

然后封装一个utils.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;
}

我们还需要进行webpack的配置,不然无法识别到我们的scss代码

所以,我只需要在vue.config.js里配置一下utils.scss的路径,就可以全局使用了

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,详细配置参考vue-cli官网
    loaderOptions:{
      sass:{
        prependData:`@import "@/styles/utils.scss";`
      }
    }
	}
}

最后在我们的vue页面中去使用

<template>
	<div class="box">			
	</div>
</template>

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

    }
</script>

<style lang="scss" scoped="scoped">
    /* 
     直接使用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>

这个方案不仅要配置webpack、安装scss,封装vw/vh的方法,还需要再vue文件中去使用vw、vh的函数去替换px的值。

我们同时还得考虑到在echarts中使用width、height等属性,因为echarts只识别px,我们这时候还得去封装js函数去把echarts中的width、height等属性转换为vw、vh

具体源码可以参考这位大佬的方案:juejin.cn/post/700908…

这个时候,大家是不是会发现好麻烦啊,并且css和js里面耦合太多函数了,很容易vw和vh就写反了,不够优雅简洁,还得安装一些第三方包,还有没有更简洁的方案呢?

使用scale方案

这个是我对比了很多方案,比较优劣之后觉得最容易上手也最容易理解的方案了

以vue3为例

// dataScreenRef为数据大屏页面的最外层div
const dataScreenRef = ref<HTMLElement | null>(null);

onMounted(() => {
	// 初始化时为外层盒子加上缩放属性,防止刷新界面时就已经缩放
	if (dataScreenRef.value) {
		dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`;
		dataScreenRef.value.style.width = `1920px`;
		dataScreenRef.value.style.height = `1080px`;
	}
	// 初始化echarts
	initCharts();
	// 为浏览器绑定事件
	window.addEventListener("resize", resize);
});
// 初始化 echarts,这个就是通过http或者websocket获取关于echarts的数据进行组装
const initCharts = () => {}

// 根据浏览器大小推断缩放比例
const getScale = (width = 1920, height = 1080) => {
	let ww = window.innerWidth / width;
	let wh = window.innerHeight / height;
	return ww < wh ? ww : wh;
};

// 浏览器监听 resize 事件
const resize = () => {
	if (dataScreenRef.value) {
		dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`;
	}
	// 使用了 scale 的echarts其实不需要需要重新计算缩放比例
	Object.values(dataScreen).forEach(chart => {
		chart && chart.resize();
	});
};

// 销毁时触发
onBeforeUnmount(() => {
	window.removeEventListener("resize", resize);\
	// 每次离开页面时,清空echarts实例,不然会出现无法显示的问题
	Object.values(dataScreen).forEach(val => {
		val?.dispose();
	});
});

通过这样几行代码就完成了一个数据化大屏的适配,比其他方案简单了很多倍,也不需要引用别的外部js文件,

这个方案可以具体参考这个开源项目:github.com/HalseySpicy…

这个是我从学习echarts到方案落地的这段时间,目前看过最满意的方案,暂时项目还没有出现什么问题。有兴趣的同学可以去看看源码!

总结:

如果大家有更好的方案,可以评论一起交流交流!!!

感谢大家!!!