ECharts海量数据渲染解决卡顿的4种方式

echarts · 浏览次数 : 17

小编点评

**压根想不到是10万条数据** 通过 dataZoom 是非常有效的 dataZoom 处理海量数据的优缺点优点: **优点:** * 配置简单:无需手动设置很多参数,方便快捷。 * 可视化整体趋势:通过设置 sampling 参数,可以查看数据的整体趋势。 * 降低数据加载时间:可以通过设置 large 参数提升图表渲染效率。 * 改善性能:可以通过设置 largeThreshold 参数优化图表渲染性能。 **缺点:** * 可看指定局部的数据:通过设置 sampling 参数只能查看指定局部的数据。 * 无法看整体数据:数据Zoom 只允许查看数据中的部分区域。 * 部分数据丢失:使用 large 参数后,部分数据可能会被截取,导致数据丢失。 * tooltip 功能可能实现不了使用 large 属性:当 large 属性设置时,tooltip 函数可能无法正常显示数据点的信息。

正文

场景

周五进行需求评审的时候;
出现了一个图表,本身一个图表本没有什么稀奇的;
可是产品经理在图表的上的备注,让我觉得这个事情并不简单;
那个图表的时间跨度可以是月,年,而且时间间隔很短;
这让我意识到事情并不是想的那样简单;
然后经过简单的询问:如果选择的范围是年;数据可能会上万;
我们都知道;出现上万的数据;
在渲染的时候肯定会出现白屏,操作的时候卡顿;
今天周末,没有事干;来研究研究 Echarts 渲染海量数据

file模块用来写文件

我们首先使用node来生成10万条数据;
借助node的fs模块就行;
如果不会的小伙伴;也不要担心;超级简单
// 引入模块
let fs = require('fs');
// 数据内容
let fileCont='我是文件内容'
/**
 * 第一个参数是文件名
 * 第二个参数是文件内容,这个文件的内容必须是字符串哈(特别注意)
 * 第三个参数是回调函数, 回调函数中有两个参数,
 * 第一个参数是错误信息,
 * 第二个参数是写入成功后的返回值
 * */ 
fs.writeFile('./demodata.txt',fileCont, (error, data) => {
  if (!error) {
    console.log('写入成功了',data)
  } else {
    console.log('写入失败了',error)
  }
})

现在我们需要创建一个指定类型的数据格式

我们等会从2022.1.1开始;每条数据间隔5分钟;产生10万条数据。
time的值是时间戳,我们可以通过 new Date().getTime() 来获取
value的值是温度,我们通过Math.random() * 50+10来获取
time的时间间隔是每隔5分钟
数据格式如下
 [
  {"time":1640966400000,"value":36.57},
  {"time":1640966700000,"value":31.68},
]
// 引入模块
let fs = require('fs');
// 生成100000条符合要求的数据格式
function timeFn(total){
  // 获取2022年1月1日的时间戳
  let dateTimeStamp = new Date(2022, 0, 1).getTime(); // 2022年1月1日 
  // 5分钟的时间戳是多少
  let oneHourStamp = 1000 * 60*5;
  let newArr = []
  for(let i= 0;i<total; i++){
    // 构造我们需要的数据格式
    newArr.push(
      { time: dateTimeStamp + oneHourStamp* i,
        value:Math.random() * 50+10 
      }
    )
  }
  return newArr
}
let needData = timeFn(100000)

fs.writeFile('./demodata.js',JSON.stringify(needData), (error, data) => {
  // JSON.stringify(needData) 将数组转为字符串
  if (!error) {
    console.log('写入成功了',data)
  } else {
    console.log('写入失败了',error)
  }
})

10万条数据渲染耗时10秒,且页面非常卡顿

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="../allechart/echarts.js"></script>
  <script src="./demodata.js"></script>
</head>

<body>
  <div style="width: 598px;height: 400px;" id="box"></div>
</body>
<script>
  function backTime(value){
    let date = new Date(value);  
    // 获取年份、月份和日期  
    let year = date.getFullYear();  
    // 月份从 0 开始,需要加 1  
    let month = date.getMonth() + 1; 
    let day = date.getDate();  
    let hours = date.getHours();  
    let minutes = date.getMinutes();  
    let seconds = date.getSeconds(); 
    // 格式化月份和日期为两位数(不足两位时补零)  
    month = month < 10 ? '0' + month : month;  
    day = day < 10 ? '0' + day : day;  
    hours = hours < 10 ? '0' + hours : hours;  
    minutes = minutes < 10 ? '0' + minutes : minutes;  
    seconds = seconds < 10 ? '0' + seconds : seconds;  
    // 返回格式化后的字符串  
    return year + '-' + month + '-' + day +  '\n' + hours + ':' + minutes + ':' + seconds;
  }
 
  console.log(backData)
  let myChart = echarts.init(document.querySelector('#box'))
  let option = {
    legend: {
      data: ['某城市']
    },
    tooltip: {
      trigger: 'axis',
      triggerOn: 'mousemove',
      confine: true,
      extraCssText: 'white-space: pre-wrap'
    },
    xAxis: {
      type: 'category',
      // 返回时间
      data: backData.map(v=> backTime(v.time)),
      splitLine: { show: false },
      lineStyle: {
        width: 2
      },
      axisTick: {
        show: false
      },
      axisLabel:{
        // 更改x轴文字颜色的配置
        textStyle: {
          color: '#717782'
        },
        showMinLabel: true,
        showMaxLabel: true // 固定显示X轴的最后一条数据
      },
    },
    yAxis: {
      type: 'value',
      axisLine: {
        show: false
      },
      axisTick: {
        show: false
      },
      splitLine: {
        lineStyle: {
          color: '#D2DBE6'
        }
      },
      axisLabel: {
        formatter: '{value}',
        color: '#717782'
      }
    },
    grid: {
      left: '30',
      right: '35',
      bottom: '10',
      top: '20',
      containLabel: true
    },
  
    series: [
      {
        data:backData.map(v=>v.value),
        type: 'line',
        smooth: true
      }
    ]
  };
  myChart.setOption(option);
</script>

</html>

发现的问题

我们发现渲染时间非常久(需要10多秒),而且页面卡顿;
有没有好的办法来解决这个问题呢;
是有的,最好的使用echarts的dataZoom用于区域缩放;
通过滑块看指定区域的数据,我们来尝试一下

dataZoom的常见属性介绍

type: "slider" || "inside",
  slider:这种类型会在图表的一侧添加一个滑动条,
  用户可以通过拖动滑动条来改变数据窗口的范围,从而实现数据的缩放。
  inside:这种类型缩放组件是内置于坐标系中的,
  用户可以通过鼠标滚轮、触屏手指滑动等方式来操作数据的缩放。
  简单点说:slider会产生一个滚动条,inside不会

xAxisIndex: 可以是一个数字,表示特定的X轴索引;
  也可以是一个数组,表示同时控制多个X轴。

xAxisIndex: 0, 控制第1条数据开始

start: 0, 数据窗口范围的起始百分比。范围是:0 ~ 100。表示 0% ~ 100%。

end: 1, 数据窗口范围的结束百分比。范围是:0 ~ 100。

minSpan: 0, 用于限制窗口大小的最小值(百分比值),取值范围是 0 ~ 100。

maxSpan: 10, 用于限制窗口大小的最大值(百分比值),取值范围是 0 ~ 100。

特别提醒:start: 设置为0;说明是从第1条数据开始的;
那么xAxisIndex就必须是0;
因为xAxisIndex不是0,他们就互相矛盾了;
minSpan 和 maxSpan一般配合使用;主要是用于只展示某一个区间;
无论用户怎么缩放;都会在这个区间

我们使用 dataZoom 来处理海量的数据

... 其他配置项
dataZoom: [
  {
    type: "slider", // 滑块类型 值有slider和inside
    xAxisIndex: [0],
    start: 0,
    end: 1,
    minSpan: 0, // 用于限制窗口大小的最小值(百分比值),取值范围是 0 ~ 100。
    maxSpan: 10,
  },
],
series: [
  {
    data:backData.map(v=>v.value),
    type: 'line',
    smooth: true
  }
]

配置之后,我们发现渲染非常流畅

通过配置前和配置后的图的对比
我们发现配置之后;页面渲染速度非常快;
打开页面就渲染完成,压根想不到是10万条数据;
说明通过 dataZoom 是非常有效的

dataZoom处理海量数据的优缺点

优点:配置简单;
缺点:只能看指定局部的数据;无法看整体数据

其他办法 sampling 降采样策略 sampling: 'average'

series: [
  {
    data:backData.map(v=>v.value),
    type: 'line',
    smooth: true,
    sampling: 'average',//' 表示采用平均值采样策略
  }
]

sampling的几个值

'lttb': 采用 Largest-Triangle-Three-Bucket 算法,
      可以最大程度保证采样后线条的趋势,形状和极值。
      不过有可能会造成页面渲染时间长
'average': 取过滤点的平均值
'min': 取过滤点的最小值
'max': 取过滤点的最大值
'minmax': 取过滤点绝对值的最大极值 (从 v5.5.0 开始支持)
'sum': 取过滤点的和

sampling 降采样策略 sampling: 'lttb'

series: [
  {
    data:backData.map(v=>v.value),
    type: 'line',
    smooth: true,
    // 采用 Largest-Triangle-Three-Bucket 算法,
    // 可以最大程度保证采样后线条的趋势,形状和极值。
    // 不过有可能会造成页面渲染时间长
    sampling: 'lttb'
  }
]

降采样策略 sampling 的优缺点

优点:可以看见整体的趋势;
缺点:部分数据丢失;tooltip功能可能实现不了

使用 large 属性

series: [
  {
    data:backData.map(v=>v.value),
    type: 'line',
    smooth: true,
    //开启大数据量优化,在数据特别多而出现图形卡顿时候开启
    large:true, 
  }
]

发现使用large属性没有效果

为什么我们使用large没有效果呢?
我们去官网看看怎么说的;
https://echarts.apache.org/zh/option.html#series-bar.type
在上面这个文档中心,我发现折线图[type: 'line']没有 large 属性
large支持的图表:柱状图(bar), K 线图 (candlestick), 路径图(lines),散点图(scatter)
其他类型的图表暂时不支持;
所以我们可以把折线图更改为柱状图看下是否可以解决卡顿问题;
series: [
  {
    data:backData.map(v=>v.value),
    type:'bar',
    //开启大数据量优化,在数据特别多而出现图形卡顿时候开启
    large:true,
  }
]

large 属性的介绍以及优缺点

large:是否开启大数据量优化;
在【数据图形】特别多而出现卡顿时候可以开启。
开启后配合 largeThreshold 在数据量大于指定阈值的时候对绘制进行优化。
优点:解决图形卡数据量大而产生的卡顿问题。
缺点:优化后不能自定义设置单个数据项的样式;
【特别提醒】: 
large支持的图表:柱状图(bar), K 线图 (candlestick), 路径图(lines),散点图(scatter)

"辅助"large属性最佳配角 largeThreshold

有些时候;数据量并不是一开始就是大量;
而是经过时间的变化;数据才变成了海量数据;
如果我们一开始就使用large开启优化;这显然是不适合的;
这个时候;我们的最佳配角 largeThreshold 就闪亮登场了;
largeThreshold:开启绘制优化的阈值。
当数据量(即data长度)大于这个阀值的时候,会开启高性能模式。
低于这个阀值;不会开启高性能模式;
比如我们希望:数据量(即data长度)大于1万时开启高性能模式,你可以这样设置:
series: [
  {
    data:backData.map(v=>v.value),
    type:'bar',
    //开启大数据量优化,在数据特别多而出现图形卡顿时候开启
    large:true,
    // 数据量大于1万时开启高性能模式,如果没有大于1万;不会开启
    // 此时我们的数据是10万;会开启;渲染非常快
    largeThreshold: 10000,
  }
]

appendData 属性的简单介绍

根据官网的介绍;
appendData属性会分片加载数据和增量渲染;
在大数据量的场景下(例如地理数的打点);
此时,数据量将会非常的大;
在互联网环境下,往往需要分片加载;
appendData 接口提供了分片加载后增量渲染的能力;
渲染新加入的数据块时,不会清除原有已经渲染的部分数据。
但是并不是所有图表类型都支持这个属性;
目前不支持series系列的;如柱状图,折线图,饼状图这些都不支持;
目前支持的图有: 散点图(scatter),线图(lines)。
ECharts GL的散点图(scatterGL)、线图(linesGL) 和 可视化建筑群(polygons3D)。
上面这段参考:https://echarts.apache.org/zh/api.html#echartsInstance.appendData

series系列的图表

尾声

最近股票亏麻了;哎;好难受;
如果觉得我的文章写的还不错;给我点个推荐;
感谢了

与ECharts海量数据渲染解决卡顿的4种方式相似的内容:

ECharts海量数据渲染解决卡顿的4种方式

场景 周五进行需求评审的时候; 出现了一个图表,本身一个图表本没有什么稀奇的; 可是产品经理在图表的上的备注,让我觉得这个事情并不简单; 那个图表的时间跨度可以是月,年,而且时间间隔很短; 这让我意识到事情并不是想的那样简单; 然后经过简单的询问:如果选择的范围是年;数据可能会上万; 我们都知道;出

Echarts设置饼状图保证你看的明明白白

简单的饼状图 EC

妙趣横生:利用Echarts实现SpreadJS引用从属关系的可视化魅力

最新技术资源(建议收藏) https://www.grapecity.com.cn/resources/ 在金融行业,我们经常会有审计审查的需求,对某个计算结果进行审查,但是这个计算结果可能依赖多个单元格,而且会有会有多级依赖的情况,如果让我们的从业人员靠眼睛找,工作量巨大,而且准确性存疑,基本上死

ECharts图表动态修改series显示隐藏

[toc] # 1、前言 ![ECharts](https://img2023.cnblogs.com/blog/2055342/202308/2055342-20230829183243791-1039279852.gif) 最近做的大数据平台,里面很多地方用到了ECharts,其中有个功能,要求

用Echarts实现前端表格引用从属关系可视化

本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 在金融行业,我们经常会有审计审查的需求,对某个计算结果进行审查,但是这个计算结果可能依赖多个单元格,而且会有会有多级依赖的情况,如果让我们的从业人员靠眼睛找,工作量巨大

如何判断一个点在地图上?如何判断一个点在多边形内?

highlight: a11y-dark 近期,有接手到一个echarts地图图表项目,因为采集的散点数据很多打不到准确的地图点上,故有了这个问题。 一般而言,标题的两个问题其是同一个问题,因为对与一个地图数据,也就是geoJson来说,其实就是一个有很多个点的多边形。 目前来说判断点是否在一个多边

项目实战:在线报价采购系统(React +SpreadJS+Echarts)

小伙伴们对采购系统肯定不陌生,小到出差路费、部门物资采购;大到生产计划、原料成本预估都会涉及到该系统。 管理人员可以通过采购系统减少管理成本,说是管理利器毫不过分,对于采购的效率提升也有极大帮助。 但是对于大多数制造业企业而言,具有企业级整体视角的管理人才仍然难得,系统化的思考方式、解决复杂业务管理

Vue第三方库与插件实战手册

这篇文章介绍了如何在Vue框架中实现数据的高效验证与处理,以及如何集成ECharts、D3.js、Chart.js等图表库优化数据可视化效果。同时,探讨了Progressive Web App(PWA)的接入与优化策略,以提升Web应用的用户体验与加载速度。

vue 中安装并使用echart

本文为博主原创,转载请注明出处: 1.安装echart 依赖: 安装命令: npm install echarts --save 在vscode 的终端窗口进行执行,如图所示: 执行完之后,查看 项目中的echart 版本依赖是否添加成功: package-lock.json 中有具体的echart

项目小结:使用Docker迁移服务到离线服务器

## 前言 最近遇到的这个场景,需要把之前开发的一套系统迁移到一个离线的服务器上,这个服务器有点麻烦,接入VPN后通过堡垒机才能访问,速度也很慢,遇到不少坑,本文记录一下迁移过程。 ## 基本信息 原本这套系统也挺简单的,Django 写的后端接口,搭配 `Vue+Echarts` 大屏,数据库用