入门
安装与第一个图表
ECharts 在带有明确宽高的 DOM 元素上初始化。setOption 配置整个图表。Apache ECharts(原百度 ECharts)是最强大的 JavaScript 图表库之一。
<!-- include ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<div id="chart" style="width: 600px; height: 400px;"></div>
<script>
const chart = echarts.init(document.getElementById('chart'));
chart.setOption({
title: { text: 'Sales' },
xAxis: { data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: {},
series: [{
type: 'bar',
data: [100, 200, 150, 300]
}]
});
</script>主题与渲染器初始化
init 接收 (dom, theme, opts) 参数。'svg' 渲染器在任何缩放级别都更清晰,且对简单图表生成更小的 DOM,而 'canvas' 对大数据集更快。devicePixelRatio 控制 retina 屏幕的清晰度。移除图表时务必调用 dispose() 释放内存。
// register a theme (defined elsewhere or imported)
echarts.registerTheme('myTheme', {
color: ['#5470c6', '#91cc75'],
backgroundColor: '#f5f5f5'
});
// renderer: 'canvas' (default, faster) or 'svg' (sharper, lighter)
const chart = echarts.init(document.getElementById('chart'), 'myTheme', {
renderer: 'svg',
devicePixelRatio: 2,
width: 800,
height: 600,
locale: 'EN'
});
// detached from DOM later
chart.dispose();响应式缩放
ECharts 不会在容器变化时自动缩放——必须调用 chart.resize()。ResizeObserver 是监听容器尺寸的现代方式(比 window resize 更适合布局驱动的变化)。在 SPA 中务必在卸载时 dispose() 以防止内存泄漏。
const chart = echarts.init(document.getElementById('chart'));
chart.setOption({ /* ... */ });
// resize when window changes
window.addEventListener('resize', () => chart.resize());
// or observe the container
const ro = new ResizeObserver(() => chart.resize());
ro.observe(document.getElementById('chart'));
// resize with explicit size
chart.resize({ width: 500, height: 300, silent: false });
// cleanup
window.addEventListener('unload', () => chart.dispose());setOption 合并模式
默认情况下 setOption 深度合并新选项到已有选项中,因此可以更新单个部分。notMerge: true 替 换整个选项(在 series 数量变化时有用)。replaceMerge 针对特定组件类型(series、xAxis)进行部分替换。lazyUpdate 将多次 setOption 调用批处理为一次渲染。
const chart = echarts.init(document.getElementById('chart'));
// merge (default): deep-merges with existing option
chart.setOption({ xAxis: { data: ['A', 'B'] } });
// notMerge: replace entire option (old config discarded)
chart.setOption({ xAxis: { data: ['X', 'Y'] } }, { notMerge: true });
// replaceMerge: replace specific component types only
chart.setOption(
{ series: [{ type: 'bar', data: [5, 6] }] },
{ replaceMerge: ['series'] }
);
// lazyUpdate: batch updates, apply on next frame
chart.setOption({ series: [{ data: [1, 2] }] }, { lazyUpdate: true });生命周期:getInstanceByDom 与 dispose
在已有图表的 DOM 上调用 init 会抛错。用 getInstanceByDom 检查。dispose() 释放内存并移除图表,DOM 元素保留。属性 _echarts_instance_ 标记图表容器——查询它可以找到并清理散落的图表。务必在框架卸载钩子中 dispose。
function mountChart(dom) {
// avoid double-init on the same DOM node
const existing = echarts.getInstanceByDom(dom);
if (existing) return existing;
const chart = echarts.init(dom);
chart.setOption({ /* ... */ });
return chart;
}
// in a framework cleanup (React useEffect / Vue onUnmounted):
function unmount(dom) {
const chart = echarts.getInstanceByDom(dom);
if (chart) chart.dispose(); // free memory + remove DOM
}
// dispose all charts on a page
echarts.disposeAll?.(); // not built-in; iterate manually
Array.from(document.querySelectorAll('[_echarts_instance_]'))
.forEach(dom => echarts.dispose(dom));柱状图
基础柱状图
柱状图需要 category xAxis 和 value yAxis。每个 series.data 值映射到一个类别。itemStyle 控制柱子外观。垂直柱状图的类别轴是 x 轴;水平柱状图则交换 x 和 y 轴类型。
chart.setOption({
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
},
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: [120, 200, 150, 80, 70],
itemStyle: { color: '#5470c6' }
}]
});分组与堆叠柱状图
同一 xAxis 上的多 个柱状图 series 默认分组并排显示。要堆叠它们,给每个 series 相同的 stack 名称。堆叠按类别累加值。在所有应堆叠的 series 上使用 stack: 'total'——没有 stack 名称的 series 仍然分组。
chart.setOption({
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
// GROUPED: multiple series render side by side
series: [
{ type: 'bar', name: 'A', data: [10, 20, 30, 40] },
{ type: 'bar', name: 'B', data: [15, 25, 35, 45] }
]
});
// STACKED: add stack with same name to each series
chart.setOption({
series: [
{ type: 'bar', name: 'A', stack: 'total', data: [10, 20, 30, 40] },
{ type: 'bar', name: 'B', stack: 'total', data: [15, 25, 35, 45] }
]
});水平柱状图
水平柱状图就是类别轴在 yAxis、值轴在 xAxis 的柱状图。position: 'right' 的标签放在柱子末端。水平柱状图更适合长类别名称或比较多个类别——避免了旋转标签。
chart.setOption({
// swap: category axis becomes yAxis
yAxis: {
type: 'category',
data: ['Apple', 'Banana', 'Cherry', 'Date']
},
xAxis: { type: 'value' },
series: [{
type: 'bar',
data: [120, 200, 150, 80],
label: {
show: true,
position: 'right' // label at end of bar
}
}]
});带背景与圆角的柱状图
showBackground 在每个柱子后面绘制轨道(进度条样式)。itemStyle.borderRadius 圆角柱子角——垂直柱子传 [左上, 右上, 右下, 左下];水平柱子顺序相反。borderRadius 可以是数字(所有角)或数组。
chart.setOption({
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D'] },
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: [50, 80, 60, 90],
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)',
borderRadius: [4, 4, 0, 0]
},
itemStyle: {
borderRadius: [8, 8, 0, 0] // round top corners
}
}]
});动态柱状图赛跑
柱状图赛跑通过动画展示重新排序。排序数据数组,然后更新 yAxis 类别和 series 数据。animationDurationUpdate 控制过渡平滑度。yAxis.inverse: true 将最大值放在顶部。这在动画数据故事中很流行。
const data = [
{ name: 'A', value: 10 },
{ name: 'B', value: 20 }
];
chart.setOption({
xAxis: { type: 'value', max: 100 },
yAxis: { type: 'category', inverse: true },
series: [{
type: 'bar',
data: data.map(d => ({ value: d.value, name: d.name })),
label: { show: true, position: 'right' },
// sort bars by value on each update
animationDuration: 500,
animationDurationUpdate: 500
}]
});
// update data on interval
setInterval(() => {
data.forEach(d => d.value += Math.random() * 10);
data.sort((a, b) => b.value - a.value);
chart.setOption({
yAxis: { data: data.map(d => d.name) },
series: [{ data: data.map(d => d.value) }]
});
}, 1000);柱宽与间距
barWidth 接受像素或类别带的百分比。barGap 是同一组内柱子之间的间距(只在多 series 时有意义)。barCategoryGap 是组之间的间距。百分比相对于类别带,因此在缩放时保持比例。
chart.setOption({
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D'] },
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: [50, 80, 60, 90],
barWidth: '40%', // width of each bar
barGap: '20%', // gap between bars in same group
barCategoryGap: '40%', // gap between category groups
// fixed pixel width also works:
// barWidth: 30
}]
});折线图
基础折线图
折线图使用 type: 'line'。默认显示 symbol 标记点;设置 symbol: 'none' 隐藏它们。线按数据顺序连接点。对于时间序列,在 xAxis 上使用 type: 'time' 配合 [时间戳, 值] 对。
chart.setOption({
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] },
yAxis: { type: 'value' },
series: [{
type: 'line',
data: [120, 200, 150, 80, 70],
symbol: 'circle', // point marker
symbolSize: 8
}]
});平滑线与面积图
smooth: true 使线条弯曲(Catmull-Rom 插值)。areaStyle 填充线下区域;渐变(线性色标)给出渐隐效果。类别轴上的 boundaryGap: false 使线从 y 轴边缘开始而不是留间隙。
chart.setOption({
xAxis: { type: 'category', boundaryGap: false, data: ['Mon','Tue','Wed','Thu','Fri'] },
yAxis: { type: 'value' },
series: [{
type: 'line',
data: [120, 200, 150, 80, 70],
smooth: true, // curved line
areaStyle: { // fill under line
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(84,112,198,0.5)' },
{ offset: 1, color: 'rgba(84,112,198,0.05)' }
]
}
}
}]
});多系列折线图
多个 series 渲染为不同的线,通过颜色区分。图例可切换可见性。lineStyle.type 接受 'solid'、'dashed'、'dotted'。每个 series 可以有自己的样式。图例中的名称必须与 series.name 匹配。
chart.setOption({
legend: { data: ['Email', 'Search'] },
xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri'] },
yAxis: { type: 'value' },
series: [
{
type: 'line',
name: 'Email',
data: [120, 132, 101, 134, 90],
lineStyle: { width: 3, type: 'solid' }
},
{
type: 'line',
name: 'Search',
data: [220, 182, 191, 234, 290],
lineStyle: { width: 2, type: 'dashed' }
}
]
});堆叠面积图
堆叠面积图使用 type: 'line' 配合相同的 stack 名称和 areaStyle。每个 series 堆叠在前一个之上。emphasis.focus: 'series' 在悬停时高亮整个 series。适用于展示随时间变化的组成(例如按来源的流量)。
chart.setOption({
xAxis: { type: 'category', boundaryGap: false, data: ['Mon','Tue','Wed','Thu','Fri'] },
yAxis: { type: 'value' },
series: [
{
type: 'line',
name: 'A',
stack: 'total',
areaStyle: {},
data: [120, 132, 101, 134, 90],
emphasis: { focus: 'series' }
},
{
type: 'line',
name: 'B',
stack: 'total',
areaStyle: {},
data: [220, 182, 191, 234, 290]
}
]
});阶梯线图
step 将线变为阶梯形。'start' 在点之前步进,'middle'(对称)在点处,'end' 在之后。阶梯图适合离散变化的值(库存水平、服务器状态)而非连续变化。
chart.setOption({
xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri'] },
yAxis: { type: 'value' },
series: [{
type: 'line',
data: [120, 200, 150, 80, 70],
step: 'middle', // 'start' | 'middle' | 'end'
symbol: 'none',
lineStyle: { width: 2 }
}]
});时间轴折线图
使用 type: 'time' 时,轴自动格式化日期并处理不规则间隔。数据点是 [ 时间戳, 值] 对。ECharts 按实际时间间隔点,因此数据中的间隙正确显示。这是间隔不等的时间序列的正确选择。
chart.setOption({
xAxis: { type: 'time' }, // auto-parses timestamps
yAxis: { type: 'value' },
series: [{
type: 'line',
data: [
[new Date('2024-01-01').getTime(), 100],
[new Date('2024-01-02').getTime(), 200],
[new Date('2024-01-05').getTime(), 150],
[new Date('2024-01-10').getTime(), 300]
],
smooth: true
}]
});饼图
基础饼图
饼图不使用坐标轴——series.data 是 {value, name} 数组。radius 以容器较小尺寸的百分比控制饼图大小。emphasis 样式在悬停时应用。图例自动从 data.name 派生标签。
chart.setOption({
series: [{
type: 'pie',
radius: '60%', // radius of pie
data: [
{ value: 1048, name: 'Search' },
{ value: 735, name: 'Direct' },
{ value: 580, name: 'Email' },
{ value: 484, name: 'Union' },
{ value: 300, name: 'Video' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0,0,0,0.5)'
}
}
}]
});环形图(圆环)
将 radius 设置为 [内半径, 外半径] 创建环形。中心标签是常见模式(显示总计或 KPI)。avoidLabelOverlap: false 保持标签在原位。内半径控制环的厚度——[50%, 70%] 是薄环;[0%, 70%] 是完整饼图。
chart.setOption({
series: [{
type: 'pie',
radius: ['40%', '70%'], // [inner, outer] = ring
avoidLabelOverlap: false,
label: {
show: true,
position: 'center', // center label
formatter: 'Total\n{c}'
},
data: [
{ value: 1048, name: 'A' },
{ value: 735, name: 'B' },
{ value: 580, name: 'C' }
]
}]
});玫瑰图(南丁格尔图)
roseType 使切片半径与值成比例(南丁格尔玫瑰)。'area' 使用 sqrt(value) 作为半径(面积比例),'radius' 直接使用值。每个切片角度相同但半径不同。适合角度难以判断的量级比较。
chart.setOption({
series: [{
type: 'pie',
radius: ['10%', '70%'],
roseType: 'area', // or 'radius'
data: [
{ value: 40, name: 'A' },
{ value: 38, name: 'B' },
{ value: 32, name: 'C' },
{ value: 30, name: 'D' },
{ value: 28, name: 'E' }
],
label: { show: true }
}]
});自定义标签饼图
label.formatter 支持模板变量:{a}(系列名)、{b}(数据名)、{c}(值)、{d}(百分比)。labelLine 控制连接线。length 是靠近切片的线段,length2 是靠近标签的线段。自定义标签使饼图更易读。
chart.setOption({
series: [{
type: 'pie',
radius: '60%',
label: {
formatter: '{b}: {d}%', // name: percentage
color: '#333',
fontSize: 14
},
labelLine: {
show: true,
length: 15, // first segment
length2: 20, // second segment
smooth: true
},
data: [
{ value: 1048, name: 'Search' },
{ value: 735, name: 'Direct' }
]
}]
});半圆饼图
组合 startAngle 和 endAngle 创建部分饼图(半圆、四分之一、仪表盘样式)。center 定位饼图——['50%','70%'] 将其下移,使 180° 饼图位于底部。部分饼图常被用作仪表盘或风格化仪表板。
chart.setOption({
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '70%'], // move down
startAngle: 180, // start at left
endAngle: 360, // end at right -> semicircle
data: [
{ value: 60, name: 'A' },
{ value: 30, name: 'B' },
{ value: 10, name: 'C' }
]
}]
});散点图
基础散点图
散点图使用两个值轴;每个数据点是 [x, y]。symbolSize 控制点大小(固定或通过函数逐点设置)。散点图揭示两个变量之间的相关性。两个轴都是 type: 'value'(数值),不同于基于类别的柱状图/折线图。
chart.setOption({
xAxis: { type: 'value' },
yAxis: { type: 'value' },
series: [{
type: 'scatter',
symbolSize: 12,
data: [
[10, 20], [15, 25], [20, 30], [25, 35],
[30, 40], [35, 45], [40, 50], [45, 55]
],
itemStyle: { color: '#5470c6' }
}]
});气泡图(按值定大小)
气泡图是点大小编码第三维度的散点图。每个点传 [x, y, size],设置 symbolSize 为返回大小的函数。函数对每个点运行,因此每个气泡独立缩放。适用于无需 3D 图表的三变量数据。
chart.setOption({
xAxis: { type: 'value' },
yAxis: { type: 'value' },
series: [{
type: 'scatter',
// each point: [x, y, size]
data: [
[10, 20, 30],
[15, 25, 60],
[20, 30, 12],
[25, 35, 45]
],
symbolSize: function (data) {
return data[2]; // use 3rd element as size
}
}]
});带 VisualMap 的散点图(按值着色)
visualMap 将数据维度映射到颜色。dimension: 2 按每个点的第三个元素(索引 2)着色。inRange.color 定义渐变。这为散点图添加第四维度(颜色)。visualMap 组件显示带滑块的图例。
chart.setOption({
xAxis: { type: 'value' },
yAxis: { type: 'value' },
visualMap: {
min: 0,
max: 100,
dimension: 2, // map color to 3rd element
inRange: { color: ['#50a3ba', '#eac736', '#d94e5d'] },
right: 10,
top: 'center'
},
series: [{
type: 'scatter',
data: [[10,20,30], [15,25,60], [20,30,12]],
symbolSize: 15
}]
});涟漪散点图(动画)
effectScatter 在每个点周围添加涟漪动画——非常适合突出关键数据点(警报、热门城市)。showEffectOn 控制涟漪何时播放。brushType: 'stroke' 更轻量;'fill' 更醒目。谨慎使用——太多涟漪会分散注意力。
chart.setOption({
xAxis: { type: 'value' },
yAxis: { type: 'value' },
series: [{
type: 'effectScatter', // animated ripple
symbolSize: function (data) { return data[2]; },
data: [[10, 20, 30], [15, 25, 60]],
showEffectOn: 'render', // 'render' or 'emphasis'
rippleEffect: {
brushType: 'stroke', // 'stroke' or 'fill'
period: 4,
scale: 3
}
}]
});大规模散点图
对于数千个点,启用渐进式渲染——ECharts 将数据分块并跨帧渲染以避免卡顿。large: true 使用单一优化的绘制调用。配合小 symbolSize 和 opacity 处理重叠绘制。这使 5 万+ 点保持流畅。
// for thousands of points, use progressive rendering
chart.setOption({
xAxis: { type: 'value' },
yAxis: { type: 'value' },
series: [{
type: 'scatter',
data: largeDataArray, // 50k+ points
symbolSize: 3,
progressive: 2000, // chunk size for progressive render
progressiveThreshold: 5000, // enable above this count
large: true, // optimize for many points
largeThreshold: 2000,
itemStyle: { opacity: 0.6 }
}]
});雷达图
基础雷达图
雷达图需要 radar.indicator 数组定义每个轴(名称和最大值)。Series 数据是匹配指标的值数组。雷达图适合比较少量项目的多个属性(玩家属性、产品对比)。max 设置每个轴的刻度。
chart.setOption({
radar: {
indicator: [
{ name: 'Speed', max: 100 },
{ name: 'Power', max: 100 },
{ name: 'Range', max: 100 },
{ name: 'Comfort', max: 100 },
{ name: 'Price', max: 100 }
]
},
series: [{
type: 'radar',
data: [{ value: [85, 70, 90, 65, 80], name: 'Car A' }]
}]
});多系列雷达图
多个数据条目渲染重叠的多边形,非常适合比较。每个条目需要与图例匹配的唯一名称。太多 series(5+)使雷达图难以阅读——多边形重叠成一团。保持 2-4 个项目以保持清晰。
chart.setOption({
legend: { data: ['Car A', 'Car B'] },
radar: {
indicator: [
{ name: 'Speed', max: 100 },
{ name: 'Power', max: 100 },
{ name: 'Range', max: 100 }
]
},
series: [{
type: 'radar',
data: [
{ value: [85, 70, 90], name: 'Car A' },
{ value: [60, 95, 75], name: 'Car B' }
]
}]
});带面积的雷达图
areaStyle 用半透明颜色填充雷达多边形,使形状更易读。lineStyle 强调轮廓。保持 areaStyle 透明度较低(0.2-0.4),以便重叠的 series 保持可见。符号标记每个顶点。
chart.setOption({
radar: {
indicator: [
{ name: 'A', max: 100 },
{ name: 'B', max: 100 },
{ name: 'C', max: 100 }
]
},
series: [{
type: 'radar',
data: [{ value: [85, 70, 90], name: 'Score' }],
areaStyle: { opacity: 0.3 },
lineStyle: { width: 2 },
symbol: 'circle',
symbolSize: 6
}]
});雷达图形状与坐标轴
shape: 'polygon'(默认)给出角度轴;'circle' 使环更平滑。splitNumber 控制网格密度。splitArea 交替颜色形成斑马效果。axisName 设置轴标签样式。这些选项控制图表的视觉结构而不触及数据。
chart.setOption({
radar: {
shape: 'polygon', // 'polygon' or 'circle'
radius: '65%',
splitNumber: 5, // grid ring count
axisName: {
color: '#333',
fontSize: 12
},
splitLine: { lineStyle: { color: '#ccc' } },
splitArea: {
areaStyle: { color: ['#fafafa', '#fff'] }
},
indicator: [
{ name: 'Speed', max: 100 },
{ name: 'Power', max: 100 }
]
},
series: [{ type: 'radar', data: [{ value: [80, 70] }] }]
});雷达极坐标半径
radius: [内半径, 外半径] 创建环形雷达(像甜甜圈)。center 定位图表。指标上的 min 设置轴的起点——当所有值都很高时有用(避免中心的小多边形)。调整 center 为轴标签留出空间。
chart.setOption({
radar: {
indicator: [
{ name: 'A', max: 100, min: 0 }, // min sets the inner bound
{ name: 'B', max: 100 },
{ name: 'C', max: 100 }
],
radius: ['20%', '70%'], // [inner, outer] for ring radar
center: ['50%', '55%']
},
series: [{
type: 'radar',
data: [{ value: [60, 70, 80] }]
}]
});热力图
笛卡尔热力图
笛卡尔热力图需要 visualMap 为单元格着色。数据为 [x, y, 值]。label: { show: true } 在单元格中打印值(只适用于小网格)。calculable: true 添加滑块手柄。热力图揭示密度模式(如每小时活动)。
const hours = ['12a','1a','2a','3a','4a','5a','6a','7a','8a','9a','10a','11a'];
const days = ['Sat','Sun','Mon','Tue','Wed','Thu','Fri'];
// data: [x, y, value]
const data = [[0,0,5],[0,1,1],[1,0,8] /* ... */];
chart.setOption({
tooltip: {},
xAxis: { type: 'category', data: hours },
yAxis: { type: 'category', data: days },
visualMap: {
min: 0, max: 10,
calculable: true,
orient: 'horizontal',
left: 'center', bottom: '5%'
},
series: [{
type: 'heatmap',
data: data,
label: { show: true }
}]
});日历热力图
日历热力图显示一年中的每日值(GitHub 风格贡献图)。calendar.range 设置年份或日期范围。数据点是 [日期字符串, 值]。visualMap 渐变定义颜色停止点。cellSize 控制方块大小——'auto' 适应容器。
const data = [];
// generate one year of data
for (let i = 0; i < 365; i++) {
const date = new Date(2024, 0, 1);
date.setDate(date.getDate() + i);
data.push([date.toISOString().slice(0,10), Math.floor(Math.random() * 100)]);
}
chart.setOption({
tooltip: {},
visualMap: {
min: 0, max: 100,
inRange: { color: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'] }
},
calendar: {
range: '2024',
cellSize: ['auto', 13],
yearLabel: { show: true }
},
series: [{ type: 'heatmap', data: data }]
});热力图项样式
itemStyle.borderColor 和 borderWidth 创建网格分隔效果(白色边框使单元格分明)。borderRadius 圆角单元格角。emphasis 样式在悬停时应用。隐藏的 visualMap(show: false)仍然驱动颜色但不显示图例。
chart.setOption({
xAxis: { type: 'category', data: ['A','B','C','D'] },
yAxis: { type: 'category', data: ['X','Y','Z'] },
visualMap: { min: 0, max: 100, show: false },
series: [{
type: 'heatmap',
data: [[0,0,80],[0,1,30],[1,0,50]],
itemStyle: {
borderColor: '#fff', // white grid lines
borderWidth: 2,
borderRadius: 4
},
emphasis: {
itemStyle: { shadowBlur: 10 }
}
}]
});打卡热力图
打卡图是 7x24 的热力图,按星期和小时显示活动。每个单元格是一个时间段。圆角 itemStyle 使其看起来像点。打卡图揭示每周模式(如服务器最忙的时候)。数据是完整的 168 格网格。
const data = [];
for (let day = 0; day < 7; day++) {
for (let hour = 0; hour < 24; hour++) {
data.push([hour, day, Math.floor(Math.random() * 50) + 1]);
}
}
chart.setOption({
xAxis: { type: 'category', data: Array.from({length:24}, (_,i) => i + 'h') },
yAxis: { type: 'category', data: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] },
visualMap: { max: 50, inRange: { color: ['#fff', '#5470c6'] } },
series: [{
type: 'heatmap',
data: data,
itemStyle: { borderRadius: 10, borderWidth: 2, borderColor: '#fff' }
}]
});地理地图上的热力图
将 geo 与 map series 结合,按值为区域着色。visualMap 驱动颜色刻度。必须先通过 echarts.registerMap('china', geoJson) 注册地图。roam: true 启用平移/缩放。这就是区域填色(按区域着色)地图的构建方式。
// requires a registered geo map (e.g. echarts.registerMap)
chart.setOption({
geo: { map: 'china', roam: true },
visualMap: {
min: 0, max: 1000,
inRange: { color: ['#e0ecf4', '#005824'] }
},
series: [{
type: 'map',
map: 'china',
data: [
{ name: 'Beijing', value: 800 },
{ name: 'Shanghai', value: 950 }
]
}]
});树图
基础树图(垂直)
树图渲染层次数据。layout: 'orthogonal' 配合 orient: 'LR' 创建从左到右的树。expandAndCollapse 让用户点击折叠/展开子树。leaves 配置叶子节点样式。树图非常适合组织结构图、文件树和分类法。
const data = {
name: 'Root',
children: [
{
name: 'Child A',
children: [{ name: 'A1' }, { name: 'A2' }]
},
{
name: 'Child B',
children: [{ name: 'B1' }]
}
]
};
chart.setOption({
series: [{
type: 'tree',
data: [data],
top: '5%', left: '10%', bottom: '5%', right: '20%',
layout: 'orthogonal', // 'orthogonal' or 'radial'
orient: 'LR', // 'LR','RL','TB','BT'
symbolSize: 10,
label: { position: 'left' },
leaves: { label: { position: 'right' } },
expandAndCollapse: true,
animationDuration: 550
}]
});水平树图
orient: 'LR' 生成水平树(根在左,叶在右)。内部节点标签位于节点左侧;叶标签在右侧——这防止与分支重叠。lineStyle 上的 curveness 创建弯曲连接器,外观更柔和。
chart.setOption({
series: [{
type: 'tree',
data: [treeData],
layout: 'orthogonal',
orient: 'LR', // left to right
symbol: 'emptyCircle',
symbolSize: 7,
label: { position: 'left', verticalAlign: 'middle', align: 'right' },
leaves: { label: { position: 'right', align: 'left' } },
lineStyle: { color: '#999', width: 1, curveness: 0.5 }
}]
});径向树图
layout: 'radial' 将根放在中心,子节点向外辐射成圆形。这在水平布局太宽的大型树中效果很好。emphasis.focus: 'descendant' 在悬停时高亮所有后代,帮助用户追踪分支。
chart.setOption({
series: [{
type: 'tree',
data: [treeData],
layout: 'radial', // circular layout
symbol: 'circle',
symbolSize: 8,
label: {
position: 'inside',
rotate: 0
},
leaves: { label: { position: 'right' } },
emphasis: { focus: 'descendant' },
lineStyle: { curveness: 0.5 }
}]
});自定义符号树图
symbol 和 symbolSize 接受函数进行逐节点自定义。这里内部节点(有子节点)是较大的矩形,叶节点是小圆形。这在视觉上区分了层级。配合 orient: 'TB' 实现经典的自上而下组织结构图。
const treeData = {
name: 'CEO',
value: 'Boss',
children: [
{ name: 'VP Engineering', children: [{ name: 'Eng Lead' }] },
{ name: 'VP Sales', children: [{ name: 'Sales Lead' }] }
]
};
chart.setOption({
series: [{
type: 'tree',
data: [treeData],
symbol: function (params) {
// custom symbol per node
return params.data.children ? 'rect' : 'circle';
},
symbolSize: function (params) {
return params.data.children ? 15 : 8;
},
orient: 'TB',
label: { position: 'top', distance: 8 }
}]
});树图展开/折叠控制
initialTreeDepth 控 制初始打开多少层(null = 全部,0 = 仅根)。点击有子节点的节点切换其子树。click 事件让您对节点选择做出反应——对下钻 UI 有用。animationDurationUpdate 使展开/折叠更平滑。
chart.setOption({
series: [{
type: 'tree',
data: [treeData],
expandAndCollapse: true,
initialTreeDepth: 2, // expand first 2 levels
collapse: 'collapseBtn',
symbol: 'circle',
animationDurationUpdate: 750
}]
});
// programmatic control via API
chart.on('click', function (params) {
if (params.componentType === 'series') {
console.log('Node clicked:', params.data.name);
console.log('Collapsed:', params.data.collapsed);
}
});旭日图
基础旭日图
旭日图是径向树,每个环代表一个层级。内部 = 父级,外部 = 子级。每个节点的值驱动其角度大小。radius: [0, '90%'] 创建完整圆盘;[内半径, 外半径] 创建环形旭日图。非常适合层次比例可视化。
const data = [{
name: 'Root',
children: [
{
name: 'A',
value: 15,
children: [{ name: 'A1', value: 5 }, { name: 'A2', value: 10 }]
},
{
name: 'B',
value: 20,
children: [{ name: 'B1', value: 20 }]
}
]
}];
chart.setOption({
series: [{
type: 'sunburst',
data: data,
radius: [0, '90%'],
label: { rotate: 'radial' }
}]
});带层级的旭日图
levels 分别配置每个环——第一个条目是根,后续条目映射到更深层级。r0/r 设置该层级的内/外半径。不同的标签旋转(径向、切向、0)使各深度的文字保持可读。这提供了细粒度的样式控制。
chart.setOption({
series: [{
type: 'sunburst',
data: sunburstData,
radius: ['15%', '90%'],
levels: [
{}, // root level (default)
{
r0: '15%',
r: '45%',
label: { rotate: 0, fontSize: 14 },
itemStyle: { borderWidth: 2 }
},
{
r0: '45%',
r: '70%',
label: { rotate: 'tangential', fontSize: 12 }
},
{
r0: '70%',
r: '90%',
label: { rotate: 'radial', fontSize: 10 }
}
]
}]
});旭日图项样式与高亮
emphasis.focus: 'ancestor' 高亮从悬停节点回到根的路径——用户看到切片在层次结构中的位置。borderWidth 在视觉上分隔切片。旭日图是交互式的:点击通常会下钻。在深层树中使用祖先高亮以保持上下文。
chart.setOption({
series: [{
type: 'sunburst',
data: data,
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
emphasis: {
focus: 'ancestor' // highlight ancestors on hover
},
highlight: {
itemStyle: { color: '#ffb' }
}
}]
});带值与排序的旭日图
sort 控制父级内切片的顺序。nodeClick: 'zoomToNode' 使点击缩放到该子树(适合探索)。minAngle 隐藏小于 5 度的切片上的标签,避免杂乱。如果省略,父节点的值会自动从子节点求和。
chart.setOption({
series: [{
type: 'sunburst',
data: [{
name: 'Root',
children: [
{ name: 'A', value: 30, children: [/* ... */] },
{ name: 'B', value: 70, children: [/* ... */] }
]
}],
nodeClick: 'zoomToNode', // click to zoom into subtree
sort: 'desc', // or 'asc', null
label: { minAngle: 5 } // hide labels on tiny slices
}]
});旭日图下钻
自定义点击处理程序让您构建自己的下钻 UX。用点击的节点替换数据以放大。如果想要'返回'按钮,保留对父节点的引用。旭日图下钻非常适合探索深层层次结构而不让屏幕过载。
const data = [/* full tree */];
let currentRoot = data[0];
chart.setOption({
series: [{
type: 'sunburst',
data: data,
nodeClick: false
}]
});
chart.on('click', function (params) {
if (params.componentType === 'series' && params.data.children) {
// manually drill down
currentRoot = params.data;
chart.setOption({
series: [{ data: [currentRoot] }]
}, { notMerge: true });
}
});桑基图
基础桑基图
桑基图显示节点之间的流。data 是节点(带名称);links 是流(源、目标、值)。每个链接的宽度与值成比例。emphasis.focus: 'adjacency' 在悬停时高亮连接的节点。适合能源、资金 或用户流可视化。
chart.setOption({
series: [{
type: 'sankey',
data: [
{ name: 'Source A' },
{ name: 'Source B' },
{ name: 'Mid C' },
{ name: 'Sink D' }
],
links: [
{ source: 'Source A', target: 'Mid C', value: 5 },
{ source: 'Source B', target: 'Mid C', value: 3 },
{ source: 'Mid C', target: 'Sink D', value: 8 }
],
emphasis: { focus: 'adjacency' },
lineStyle: { color: 'gradient', curveness: 0.5 }
}]
});桑基图节点层级
levels 按深度(列)设置节点样式。lineStyle.color: 'source' 使链接颜色与源节点匹配;'target' 与目标匹配;'gradient' 混合。nodeAlign 在列内垂直定位节点。显式设置 depth 强制列放置,覆盖自动布局。
chart.setOption({
series: [{
type: 'sankey',
data: [
{ name: 'A', depth: 0 },
{ name: 'B', depth: 1 },
{ name: 'C', depth: 2 }
],
links: [
{ source: 'A', target: 'B', value: 10 },
{ source: 'B', target: 'C', value: 10 }
],
levels: [
{ depth: 0, itemStyle: { color: '#fbb' }, lineStyle: { color: 'source' } },
{ depth: 1, itemStyle: { color: '#bfb' }, lineStyle: { color: 'target' } },
{ depth: 2, itemStyle: { color: '#bbf' } }
],
nodeAlign: 'justify' // 'left','right','justify'
}]
});桑基图节点样式与间距
nodeWidth 设置每个节点矩形的厚度。nodeGap 控制垂直间距。较小的间距显示更多节点但可能重叠。lineStyle.opacity 使流变淡以突出节点。label.position: 'right' 将标签放在节点旁边——常用于从左到右的流。
chart.setOption({
series: [{
type: 'sankey',
data: nodes,
links: links,
nodeWidth: 20, // width of each node rectangle
nodeGap: 10, // vertical gap between nodes
nodeAlign: 'justify',
label: {
fontSize: 12,
color: '#333',
position: 'right'
},
lineStyle: { opacity: 0.4 },
itemStyle: { borderWidth: 0 }
}]
});带循环的桑基图(回流)
真正的循环会导致 ECharts 桑基图失败。解决方法是将循环节点拆分为单独的'入'和'出'节点(如 'Recycle'),使流无环。这在视觉上表示反馈循环。始终验证流量守恒:节点的输入总计应等于输出总计。
// Sankey normally forbids cycles, but you can show feedback loops
// by splitting a node into two (in & out) with the same name suffix
chart.setOption({
series: [{
type: 'sankey',
data: [
{ name: 'Input' },
{ name: 'Process' },
{ name: 'Output' },
{ name: 'Recycle' }
],
links: [
{ source: 'Input', target: 'Process', value: 100 },
{ source: 'Process', target: 'Output', value: 80 },
{ source: 'Process', target: 'Recycle', value: 20 },
{ source: 'Recycle', target: 'Process', value: 20 }
],
emphasis: { focus: 'adjacency' }
}]
});桑基图交互
桑基图点击事件通过 dataType 区分节点和边。用此构建显示流细节的详情面板。label.formatter: '{b}: {c}' 显示名称和值。adjacency 焦点高亮通过悬停节点的完整路径,揭示因果关系链。
chart.setOption({
series: [{
type: 'sankey',
data: nodes,
links: links,
emphasis: {
focus: 'adjacency',
lineStyle: { opacity: 0.8 }
},
label: { formatter: '{b}: {c}' },
tooltip: { trigger: 'item' }
}]
});
chart.on('click', function (params) {
if (params.dataType === 'edge') {
console.log('Flow from', params.data.source, 'to', params.data.target);
console.log('Value:', params.data.value);
} else if (params.dataType === 'node') {
console.log('Node:', params.data.name);
}
});漏斗图
基础漏斗图
漏斗图显示每个阶段项目递减的过程。sort: 'descending'(默认)将最大阶段放在顶部。数据应按流程顺序排列。漏斗图非常适合销售管道、转化分析和引导步骤。
chart.setOption({
series: [{
type: 'funnel',
data: [
{ value: 100, name: 'Visit' },
{ value: 75, name: 'Sign Up' },
{ value: 50, name: 'Trial' },
{ value: 25, name: 'Purchase' }
],
sort: 'descending' // 'descending','ascending','none'
}]
});漏斗图排序与间距
sort: 'ascending' 反转漏斗(最小在顶部,如增长图)。gap 分隔切片以提高可读性。funnelAlign 水平定位切片。minSize/maxSize 以百分比限制切片宽度,确保小阶段保持可见。
chart.setOption({
series: [{
type: 'funnel',
data: data,
sort: 'ascending', // smallest at top
gap: 4, // pixel gap between slices
funnelAlign: 'center', // 'left','center','right'
width: '60%',
minSize: '20%',
maxSize: '100%'
}]
});漏斗图标签
position: 'inside' 在切片上叠加标签;'left'/'right' 配合连接线(labelLine)放在外面。formatter: '{b}: {c}' 显示名称和值。当切片太薄无法容纳内部文字时使用外部标签。Emphasis 在悬停时放大标签。
chart.setOption({
series: [{
type: 'funnel',
data: data,
label: {
show: true,
position: 'inside', // 'inside','left','right'
formatter: '{b}: {c}'
},
labelLine: {
show: true,
length: 20,
lineStyle: { width: 1, type: 'solid' }
},
emphasis: {
label: { fontSize: 16 }
}
}]
});漏斗图项样式
逐切片 itemStyle 覆盖默认颜色循环。borderColor 在视觉上分隔切片。也可以在单个数据项上设置颜色。为了精致外观,使用顺序调色板(早期阶段较浅,转化阶段较深)以强化漏斗隐喻。
chart.setOption({
series: [{
type: 'funnel',
data: [
{ value: 100, name: 'A', itemStyle: { color: '#5470c6' } },
{ value: 75, name: 'B', itemStyle: { color: '#91cc75' } },
{ value: 50, name: 'C', itemStyle: { color: '#fac858' } }
],
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
borderRadius: 0
}
}]
});对比漏斗图
多个漏斗 series 可以并排放置以进行期间比较。给每个 series 自己的 left/width 来定位。这对于 A/B 测试可视化或年度转化对比非常有用。每个 series 使用对比色以区分它们。
chart.setOption({
series: [
{
type: 'funnel',
name: '2023',
data: [{ value: 100, name: 'A' }, { value: 40, name: 'B' }],
sort: 'descending',
left: '5%', width: '40%',
label: { position: 'inside' }
},
{
type: 'funnel',
name: '2024',
data: [{ value: 120, name: 'A' }, { value: 60, name: 'B' }],
sort: 'descending',
left: '55%', width: '40%',
label: { position: 'inside' }
}
]
});仪表盘
基础仪表盘
仪表盘在弧上显示单个值。min/max 设置刻度。progress 显示到值的彩色弧(axisLine 作为轨道)。detail 在中心渲染值文本。valueAnimation 平滑过渡数字。适合 KPI 和单指标仪表板。
chart.setOption({
series: [{
type: 'gauge',
min: 0,
max: 100,
progress: { show: true, width: 18 },
axisLine: { lineStyle: { width: 18 } },
detail: {
valueAnimation: true,
formatter: '{value}%'
},
data: [{ value: 70, name: 'Score' }]
}]
});带指针的仪表盘
指针样式是经典仪表盘外观。anchor 是中心销。负的 distance 值将刻度/标签向内推(朝向中心)。radius 缩放整个仪表盘。将指针与 splitLine 结合模仿物理仪器。当需要模拟感时有用。
chart.setOption({
series: [{
type: 'gauge',
radius: '60%',
min: 0, max: 100,
pointer: { show: true, length: '60%', width: 5 },
anchor: { show: true, size: 12, itemStyle: { color: '#333' } },
axisTick: { distance: -15, length: 5 },
splitLine: { distance: -20, length: 10 },
axisLabel: { distance: -25 },
data: [{ value: 42 }]
}]
});带色带的仪表盘
axisLine.lineStyle.color 接受 [阈值, 颜色] 对数组来创建彩色区域(如风险级别的红/黄/绿)。阈值是分数(0-1)。这是在仪表盘上显示'安全/警告/危险'范围的标准方式。指针或进度弧指示这些区域内的当前值。