ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Echarts热力图配置详解:从数据格式到visualMap实战

Echarts热力图配置详解:从数据格式到visualMap实战 做前端可视化这几年Echarts热力图是我使用率最高的图形之一。它用色块颜色深浅来映射数值强弱一眼扫过去就知道“哪里高、哪里低”非常适合信号覆盖、客流分布、负载监控这类场景。这篇把Echarts热力图的配置项从零到一拆透从series-heatmap的数据格式、visualMap的颜色映射与阈值设置到坐标轴、tooltip细节再结合两个实际案例业务时段热力图、地图热力图给出完整代码最后附上我踩过的坑和排查思路。不管你是刚接触Echarts的新手还是已经写了几个大屏但总在热力图上栽跟头的老手这篇都能直接拿来用。很多人一提热力图就只想到“在地图上画一团红的绿的”其实热力图的应用范围远不止地理场景。它本质上是一种用颜色编码数值的矩阵可视化技术数据密度高、趋势对比直观是数据可视化大屏里的常客。这篇我就围绕热力图配置项展开尽量说人话把每个参数背后的逻辑讲清楚。1. 先把热力图的定位搞明白1.1 热力图、色斑图与密度图是什么关系先理清几个容易混的概念。Echarts里官方叫法是Heatmap中文一般叫热力图。气象行业喜欢叫“色斑图”比如降水预报图、温度分布图本质就是热力图加了一个地理底图。还有一部分人管散点密度图叫热力图比如地图上那种“信号热力图”点是随机分布、按密度着色的这个在Echarts里对应的是series-scatter配合visualMap实现不是我们今天讲的series-heatmap。在实际项目中我见过不少把两种图混着用的。series-heatmap的特点是有明确的行列坐标轴数据是一格一格的比如“星期 x 小时”的流量矩阵、CPU核数 x 时间的利用率矩阵。而散点热力图是连续坐标数据点没有固定网格适合做轨迹、信号覆盖这类空间分布。搞清楚这个区别很重要因为配置项完全不同。如果你拿散点的数据格式去配heatmap图表基本不会正常渲染。我做这个区分是希望大家在选型的时候心里有数不然后面改配置改到怀疑人生。1.2 Echarts热力图的核心配置链路Echarts里实现一张热力图核心就是两条配置链路一条是series-heatmap负责画格子另一条是visualMap负责给格子填颜色。这两条链路缺一不可。const option { xAxis: { type: category }, yAxis: { type: category }, visualMap: { min: 0, max: 100, inRange: { color: [...] } }, series: [{ type: heatmap, data: [[x索引, y索引, value], ...] }] }xAxis和yAxis都是类目轴决定了热力图的网格结构。series.data里的前两个值不是具体坐标而是类目的索引值第三个值才是要映射颜色的数值。visualMap则负责把数值区间映射成颜色渐变。还有一个隐藏链路是geo。当地理坐标系的热力图比如echarts中国地图上的热力分布就得用coordinateSystem: geo把heatmap绑定到地理坐标系上这时data格式也会发生变化后面实操部分我会详细展开。1.3 适用场景与不适合场景我自己的判断标准是数据能被整理成“两个维度 一个数值”的结构就适合用热力图。两个维度比如星期和时段、日期和小时、CPU核数和时间点一个数值就是你想观察的指标比如请求量、耗电量、在线人数。反过来如果只有一个维度的变化或者维度超过三个热力图就很别扭。只有一个维度直接画折线图、柱状图更直观维度太多热力图会变成“花格子衬衫”人眼根本读不出信息。还有如果数据量特别稀疏比如一个几十乘几十的矩阵里只有三五个有值那也别用热力图一眼望去全是背景色没有意义。当时我做一个全国性的网络质量监控大屏涉及几百个地市的上百个指标把指标切成“城市 x 响应时长”的矩阵热力图选点非常合适但是后来数据维度增加到十几个指标矩阵一下就乱了。最后是把指标拆成多个热力图用tab切换才解决。2. Echarts热力图配置项逐项拆解2.1 series-heatmap数据格式与基础属性先看series-heatmap本身。它的核心属性不多但每个都很关键。series: [ { type: heatmap, data: [ [0, 0, 5], [0, 1, 12], [1, 0, 8], [1, 1, 20] ], label: { show: true, color: #333 }, itemStyle: { borderWidth: 1, borderColor: #fff, borderRadius: 2 }, emphasis: { itemStyle: { shadowBlur: 8, shadowColor: rgba(0, 0, 0, 0.3) } } } ]data数组里的每一项都是一个长度为3的小数组顺序固定第一个是x轴类目索引第二个是y轴类目索引第三个是映射颜色的值。索引从0开始对应xAxis.data和yAxis.data里的第几个类目。顺序千万不能搞反我第一次写的时候把经度纬度习惯带进来了结果图直接乱掉。label.show控制是否在格子上显示数值。数据量小的时候显示数值是很加分的一眼能看到具体是多少数据量一大就要关掉否则标签重叠看起来像一锅粥。itemStyle里的borderWidth和borderColor是给格子加边框白色边框配合深色格子会形成一种“九宫格”的视觉分隔观感会好很多。borderRadius可以让格子变成圆角适合数据量少、偏展示型的场景。强调态emphasis.itemStyle是鼠标悬停时的效果热力图默认悬停会有个高亮我习惯加一层阴影让它更明显。2.2 visualMap颜色映射与threshold阈值设置visualMap是热力图真正的灵魂。它把数值翻译成颜色直接决定了整张图的观感。visualMap: { type: continuous, min: 0, max: 100, calculable: true, orient: vertical, left: right, top: middle, inRange: { color: [#313695, #4575b4, #74add1, #abd9e9, #fee090, #fdae61, #f46d43, #d73027] } }min和max就是阈值边界。这两个值设得好不好直接决定热力图能不能看出层次。如果业务上数值是0到100你只设到100但实际数据最大只有10那图上所有格子都接近同一个颜色等于白画。反过来max设太小超过的数据会被统一映射成最深色也丢信息。设置threshold的一般做法是先看一下数据分布。我当时做流量热力图请求量中位数是3000但偶尔有峰值到5万如果max直接设5000090%的格子都淡得看不见。后来我按百分位取max设为P95的值也就是12000虽然峰值格子会“顶格变红”但这个视觉反差反而挺合理用户看到几个红点就知道发生了异常。calculable: true会在图表上生成一个可拖动的滑块用户可以直接拖动阈值范围实时观察不同数值区间的分布情况。交互上非常实用但要注意如果图表空间很局促滑块会挤占布局。inRange.color是核心参数一个颜色数组按数值从低到高排列。选色时有个原则数值低的一头用冷色蓝、青数值高的一头用暖色黄、橙、红这是人类视觉习惯看到红色会下意识觉得“高、危险”。中间可以穿插几个过渡色让变化更平滑。如果你需要分级着色比如信号强度大于-60显示绿色-60到-90显示黄色小于-90显示红色那就把type改成piecewise用pieces阈值区间来精确控制visualMap: { type: piecewise, pieces: [ { min: -90, max: -70, color: #d73027 }, { min: -70, max: -50, color: #fdae61 }, { min: -50, max: 0, color: #1a9850 } ] }这种分段模式在“相关热力图阈值”这种需要对特定数值区间做告警或分级的场景里特别好用比连续渐变更符合业务直觉。2.3 xAxis / yAxis热力图的坐标轴细节热力图的坐标轴跟普通柱状图折线图不太一样必须用类目轴而且一般都要开boundaryGap。xAxis: { type: category, data: [周一, 周二, 周三, 周四, 周五, 周六, 周日], splitArea: { show: true }, axisLine: { lineStyle: { type: solid } } }, yAxis: { type: category, data: [0点, 1点, 2点, 3点, 4点], splitArea: { show: true } }boundaryGap默认是true意思是一个类目占据一个区间段格子跟格子之间不会有空隙。如果你把它设成false格子就压缩成一条线热力图直接变成“斑马线”这不是我们想要的。坑爹的是柱状图里boundaryGap默认是true折线图默认是false很多人从柱状图切过来就忘了这个差异。splitArea.show: true会给每个类目区域画淡色的分割背景这样热力图的每个格子边界会更清楚尤其在没有格子边框的时候特别有用。但注意splitArea的颜色要调淡一点不然格子本身有颜色背景又有颜色两层层叠看起来脏兮兮的。坐标轴类目如果特别多比如x轴是24小时、y轴是60分钟那就是1440个格子标签肯定放不下。这时候要么开axisLabel.interval控制显示间距要么用dataZoom组件让用户可以缩放查看。Echarts的dataZoom在热力图上跟普通图表用法一样直接配置type: inside或slider就能对x轴做区域缩放大数据量时非常实用。2.4 tooltip气泡内容、换行与联动热力图的tooltip默认只显示x轴、y轴和数值格式是固定的。实际项目中往往需要显示更多上下文信息比如“星期一的12点请求量是3500环比上升12%”这就要重写formatter。tooltip: { trigger: item, formatter: function(params) { var xLabel params.name[0]; var yLabel params.name[1]; var value params.value[2]; return [ yLabel xLabel, 请求量 value QPS, 环比 (value 3000 ? 12% : -3%) ].join(br); } }注意params.name在热力图里是一个数组params.name[0]是x轴类目名params.name[1]是y轴类目名这点跟普通图表不一样我第一次写的时候还以为name是字符串。还有一个高频需求是tooltip自动换行。Echarts的tooltip默认宽度是根据内容自适应如果内容太长尤其在大屏上中文和英文混排气泡会横向拉得很宽非常难看。解决办法是在formatter返回的HTML里包一层div限制最大宽度并设置换行formatter: function(params) { return div stylemax-width: 240px; white-space: normal; word-break: break-all; 设备编号 params.value[0] br 信号强度 params.value[2] dBmbr 说明该设备信号衰减较明显 /div; }另外如果你的大屏是多个图表联动热力图的tooltip也可以配合dispatchAction来实现联动效果。比如鼠标悬浮在某个格子上同时高亮旁边的柱状图对应的柱子实现思路是在热力图的mouseover事件里手动触发柱状图的highlight动作配置不复杂但很出效果。3. 实操一用热力图做一周分时段流量矩阵3.1 数据组织先来一个最常用的业务场景一周7天、每天24小时的网站流量热力图。这种图适合看“什么时候是流量高峰”“周末和工作日的节奏差异”信息密度很高。数据组织上我习惯用一个二维数组行是小时列是星期。然后遍历生成data数组var days [周一, 周二, 周三, 周四, 周五, 周六, 周日]; var hours []; for (var h 0; h 24; h) { hours.push(h :00); } var matrix [ // 00:00, 01:00, ..., 23:00 [120, 80, 60, ...], [140, 90, 50, ...], // 共7行对应7天 ]; var data []; for (var i 0; i days.length; i) { for (var j 0; j hours.length; j) { data.push([j, i, matrix[i][j]]); } }这里[j, i, matrix[i][j]]第一个值是x轴索引小时第二个是y轴索引星期我为了可读性把i换成j大家理解坐标系就行。matrix的维度是 7 x 24正好对应坐标轴。3.2 完整配置代码var myChart echarts.init(document.getElementById(main)); myChart.setOption({ tooltip: { trigger: item, formatter: function(params) { return params.name[1] params.name[0] br访问量 params.value[2] 次; } }, grid: { left: 80, right: 30, top: 30, bottom: 40 }, xAxis: { type: category, data: hours, name: 小时, nameLocation: middle, nameGap: 30, splitArea: { show: true } }, yAxis: { type: category, data: days, name: 星期, nameLocation: middle, nameGap: 40, splitArea: { show: true } }, visualMap: { min: 0, max: 500, calculable: true, orient: horizontal, left: center, bottom: 0, inRange: { color: [#313695, #74add1, #fee090, #f46d43, #d73027] } }, series: [{ type: heatmap, data: data, label: { show: true, color: #fff, fontSize: 12 }, itemStyle: { borderWidth: 2, borderColor: #fff, borderRadius: 4 } }] });这里grid要留出足够的边距因为坐标轴有名称。visualMap我放在底部水平方向适合横向矩形布局。label.show我开了数据量是7x24168个点格子不算多能显示数值大屏上看得清如果你换成实时监控数据比如30天x24小时720个点就建议关掉label。3.3 配置项说明与效果调整你运行之后会发现格子是有圆角的这是borderRadius的效果数据量少的时候会显得精致。如果你希望更“数据密集”的感觉把borderRadius改为0borderWidth改为1会变成标准的网格热力图。关于visualMap的max值我前面说过要用数据分布来定。上面代码里max设500是因为matrix里最高的峰值我压到了480。如果你的数据峰谷差异特别大可以先跑一遍统计再定阈值不要拍脑袋。还有一种常见需求是只要“高于某个阈值才显示颜色”比如访问量低于50的格子全显示灰色。实现方式是用visualMap的visualRange或者formatter来做。直接的做法是给数据做一些预处理把低于阈值的值设成null配合visualMap的enable逻辑可以做出“有颜色的区域才是有效区域”的效果。4. 实操二热力图叠加地图做地理分布4.1 echarts中国地图的地理数据准备地理热力图是另一个高频场景。很多人想做信号热力图、客流量热力图一听要搞地图数据就头大。Echarts 5和之前的版本有个重要的区别官方包不再内置中国地图的geoJSON数据我用echarts 5之后每次都需要单独准备地理数据。最稳妥的办法是找一个china.json文件放到项目里然后用echarts.registerMap注册。注册的方式很简单import chinaGeo from ./assets/china.json; echarts.registerMap(china, chinaGeo);如果你用的是CDN方式引的echarts不能直接用import也要通过url获取json再注册。我通常直接放到public目录下用fetch加载。4.2 geo坐标系下的热力图层有了地图底图之后热力图需要从默认的笛卡尔坐标系切换到地理坐标系关键配置是coordinateSystem: geo同时data格式变成[经度, 纬度, value]。option { geo: { map: china, roam: true, zoom: 1.2, itemStyle: { areaColor: #1a1a2e, borderColor: #333 } }, visualMap: { min: 0, max: 100, calculable: true, inRange: { color: [#313695, #74add1, #fee090, #f46d43, #d73027] } }, series: [{ type: heatmap, coordinateSystem: geo, data: [ [116.4074, 39.9042, 85], [121.4737, 31.2304, 70], [113.2644, 23.1291, 45] // ...更多城市数据 ], pointSize: 8, blurSize: 15 }] };这里有个注意点coordinateSystem: geo下的heatmap渲染的不再是矩形格子而是圆形的散点融合效果点与点之间会通过模糊半径产生连续的渐变这就是很多人说的“热力图”效果在气象里叫“色斑图”。pointSize控制单个点的影响范围blurSize控制模糊半径这两个参数直接影响图的平滑程度。我踩过一个坑geo配置和series里的geoIndex搞混。如果你的图表里既有geo组件又有grid坐标系Echarts会默认把heatmap渲染到grid上导致地图热力不显示。解决方法是明确指定geoIndex: 0或者在series里单独用geo: {}内联配置。我习惯只在option顶层配geo然后series里写coordinateSystem: geo这样最不容易出错。4.3 色斑图与渐变调整很多做气象、环境监测的朋友跟我聊过“色斑图”的配置需求要求颜色平滑过渡不能有马赛克一样的格子感。在地理坐标系里把pointSize调大、blurSize调大颜色就自然融合了。如果要做更精细的“信号热力图阈值分级”比如信号强度在不同区间的颜色要严格区分就不适合用continuous的visualMap改成piecewise分段会更准。我做过一个4G信号覆盖图就是把强度分成四个区间极好、良好、一般、弱覆盖每个区间一个颜色再用pieces精确控制边界。还有一个技巧地理底图颜色要压暗。热力图是前景地图是背景如果底图颜色太鲜艳热力的颜色会被“抢戏”。一般我把geo.itemStyle.areaColor设为深色深蓝、深灰borderColor设为对比色让地图只提供一个轮廓参考视觉重心完全给到热力层。5. 配置中的视觉和性能细节5.1 色板选色的经验热力图的色板选得好不好直接影响整张图的可读性。我总结了一套自己的配色经验分享出来少用彩虹色。虽然Echarts默认的[#313695, ..., #d73027]这种效果不错但有些开发者随手从网上复制一个七彩虹色板颜色太多反而让“高”“低”的判断变模糊。推荐“感知均匀”的渐变色。比如[#2c7bb6, #abd9e9, #ffffbf, #fdae61, #d7191c]这是科学可视化里经典的色带浅色在中间两端深色对于有正负值或对称阈值的数据非常合适。深色背景下用亮色渐变。大屏通常用深色背景这时候用[#000004, #2c115f, #721f81, #b73779, #f1605d, #fbc7a3]这种从黑到紫到橙的渐变对比度高、又不刺眼。色盲友好。如果你不确定受众是否有色觉障碍最好避开“红绿”对比。选择蓝橙、蓝黄这类色系更安全。5.2 大数据量场景下的性能优化热力图的数据量上来之后性能会肉眼可见地下降。我做过一个设备状态监控x轴是500台设备、y轴是1440分钟总共72万个数据点第一次渲染直接卡了2秒多拖拽缩放也非常卡。优化思路有三条线第一降采样。如果数据本身是高密度矩阵先用算法把数据点减下来比如对相邻N个点取平均值再喂给Echarts。这个在数据预处理阶段做效果最明显。第二关闭渲染内耗。在series上设置animation: false避免每次数据变化都触发动画large: true开启大数据量模式几千个格子以上会有明显提升。第三用dataZoom做可视化切片。不要一次性渲染全部数据先渲染当前可视区域配合dataZoom的filterMode: none属性让用户在缩放的区域里去看细节。这样初始渲染的数据量小了交互也流畅。5.3 大屏适配与pxtorem的坑大屏项目里适配是个绕不开的话题。有人给Echarts容器设置了固定px宽度结果屏幕分辨率一变图就溢出有人用rem跟着根元素缩放结果Echarts内部canvas绘制的像素没有跟着缩放图表变得“忽大忽小”。我在vue3项目里就遇到过一个典型问题postcss-pxtorem对Echarts没起到效果。原因是pxtorem只处理样式表里的px单位而Echarts通过canvas渲染的图形坐标是js在运行时直接写入的像素值根本不会经过css编译这一层。所以你就算把容器字体改成remcanvas里画的格子位置还是按像素算的。正确的适配姿势是监听容器宽度变化动态调用chart.resize()并且把关键配置项比如visualMap的left、grid的间距用百分比或回调函数计算。还有一个办法是用Echarts的convertToPixel接口把需要“像素级联动”的坐标换算出来。我用得最多的还是百分比宽度 resize监听 关键词参数用计算函数生成三个组合基本能适配所有大屏。6. 常见问题与排查技巧实录6.1 问题速查表我把这些年做热力图遇到的典型问题整理成一个速查表大家可以对照排查现象可能原因解决办法热力图一片空白series.data里没有数据或格式不对检查data每项必须是[xIndex, yIndex, value]颜色没有渐变全是一种色visualMap缺失或min/max设置不合理补上visualMap按数据实际范围设置min/max格子错位行列对不上索引顺序写反确认xAxis是第一个下标yAxis是第二个下标地图热力不显示没注册geoJSON或coordinateSystem没设为geo用registerMap注册地图series加coordinateSystem: geotooltip内容挤成长条默认宽度未限制formatter返回div并设置max-width和white-space数据量大时卡顿未开大图模式和关动画设置large: true, animation: false配合dataZoom坐标轴标签挤在一起类目太多或标签太密用axisLabel.interval或dataZoom缩放大屏缩放后图形错位没监听resize或用了固定px加resize监听用百分比布局关键参数动态计算6.2 排查思路示例举一个实际案例。有个朋友的项目里热力图在本地开发环境显示正常一放到大屏服务器上就变成空白。他排查了很久最后发现远程环境里报错是Map china not exists问题在于Echarts 5不再内置中国地图geoJSON他本地是通过某个插件自动注册的服务器上没装那个插件所以地图组件直接报错。这类问题本质上是“环境依赖不一致”。我的建议是凡是地图类图表一律在入口文件里显式执行echarts.registerMap并打包进业务代码不要依赖任何CDN数据源。这样不管是本地、测试还是生产环境行为一致排查起来也省心。还有一个高频问题是“热力图格子颜色都是最小值的颜色”。之前有个同事做的告警热力图无论数据怎么变化所有格子颜色都一样。一查发现visualMap的max设成了100000而实际数据只有几十映射结果自然全在低值段。把max改成50之后图马上就正常了。排查visualMap相关问题时我通常会先在图表旁边临时加一个calculable: true的滑块手动拖一下看颜色变化如果能变化说明映射逻辑正常问题就在min/max范围上如果滑块拖动没反应那就是visualMap的数据映射链路有问题。这个方法特别快建议大家试试。6.3 我再补几个独家避坑技巧热力图数据里如果有异常值和缺失值别急着塞进去。缺失值如果设成0会跟真实低值混淆设成nullEcharts默认会用最小颜色填充也会误导。我一般做法是数据清洗阶段把无效点标记为null然后在visualMap的formatter或者tooltip里做特殊说明让看板用户知道这块是“无数据”而不是“低值”。如果要在同一个页面上放多张热力图对比比如不同机房的流量热力图并排展示每张图尽量用相同的visualMap范围否则颜色深浅无法互相比较。这个我在做监控大屏时深有体会当时两屏图各自自适应min/max结果A机房50的流量是红色B机房50的流量是蓝色用户一眼看过去全乱了。后来统一设置成全局阈值就再也没有这种误解。结语我的热力图配置心得做Echarts热力图的这几年我最大的感觉是热力图的上手门槛不高但真正做好看的坑是真的多。很多人觉得配置项不就那几个嘛但实际一跑起来不是颜色不对就是位置错乱或者大屏一缩放全废。我的经验是先把series-heatmap的数据格式吃透再把visualMap的映射逻辑想明白这两步走稳了热力图基本就成功了一大半。至于颜色、性能、联动这些都是在此基础上的加分项。最后再分享一个小技巧Echarts官方的示例编辑器和社区里有大量热力图相关作品遇到不懂的配置项最快的办法就是去社区找一个相似效果把它的option拿下来用你的数据跑一遍边跑边改比死记API文档高效得多。如果你正在做信号热力图、客流热力图或者任何需要“看分布”的场景希望这篇文章能帮你避开我踩过的那些坑。有更好的配色方案或者性能优化思路也欢迎交流。
返回列表