ECharts 数据与交互
本文档介绍 ECharts 中的数据处理和交互功能,包括视觉映射、图例组件、事件处理、异步数据加载和动态更新。
数据的视觉映射
数据可视化是数据到视觉元素的映射过程。ECharts 提供了 visualMap 组件来实现通用的视觉映射。
可用的视觉元素
- 图形类别(symbol)
- 图形大小(symbolSize)
- 颜色(color)
- 透明度(opacity)
- 颜色透明度(colorAlpha)
- 颜色明暗度(colorLightness)
- 颜色饱和度(colorSaturation)
- 色调(colorHue)
连续型视觉映射
javascript
option = {
visualMap: {
type: "continuous",
min: 0,
max: 5000,
dimension: 3,
seriesIndex: 4,
inRange: {
color: ["blue", "#121122", "red"],
symbolSize: [30, 100]
},
outOfRange: {
symbolSize: [30, 100]
}
}
}分段型视觉映射
javascript
option = {
visualMap: {
type: "piecewise",
pieces: [
{ min: 1500 },
{ min: 900, max: 1500 },
{ min: 310, max: 1000 },
{ min: 200, max: 300 },
{ min: 10, max: 200, label: "10 到 200(自定义标签)" },
{ value: 123, label: "123(自定义特殊颜色)", color: "grey" }
]
}
}类别型视觉映射
javascript
option = {
visualMap: {
type: "piecewise",
categories: ["严重", "重要", "一般", "轻微"],
inRange: {
color: ["#d94e5d", "#eac736", "#50a3ba", "#91cc75"]
}
}
}图例组件
图例是图表中对内容区元素的注释,用不同形状、颜色、文字等来标示不同数据列。
基本图例
javascript
option = {
legend: {
data: ["图例一", "图例二", "图例三"]
},
series: [
{
name: "图例一",
type: "bar",
data: [10, 20, 30]
}
]
}图例布局
javascript
option = {
legend: {
orient: "vertical", // 'horizontal' 或 'vertical'
left: "right", // 左侧位置
top: "center" // 垂直位置
}
}可滚动图例
当图例较多时,可以使用可滚动翻页的图例:
javascript
option = {
legend: {
type: "scroll",
orient: "vertical",
right: 10,
top: 20,
bottom: 20,
data: ["图例一", "图例二", "图例三", "..."]
}
}图例样式
javascript
option = {
legend: {
data: ["图例一", "图例二", "图例三"],
icon: "rect", // 图例图标形状
backgroundColor: "#ccc",
textStyle: {
color: "#333"
}
}
}图例交互
javascript
option = {
legend: {
data: ["图例一", "图例二", "图例三"],
selected: {
图例一: true,
图例二: true,
图例三: false
}
}
}不同类型的图例样式
javascript
option = {
legend: {
data: [
{
name: "图例一",
icon: "rect"
},
{
name: "图例二",
icon: "circle"
},
{
name: "图例三",
icon: "pin"
}
]
}
}事件处理
在 ECharts 的图表中,用户的操作会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理。
鼠标事件
ECharts 支持常规的鼠标事件类型:
'click'- 点击'dblclick'- 双击'mousedown'- 鼠标按下'mousemove'- 鼠标移动'mouseup'- 鼠标释放'mouseover'- 鼠标悬停'mouseout'- 鼠标移出'globalout'- 全局移出'contextmenu'- 右键菜单
基本事件绑定
javascript
myChart.on("click", function (params) {
console.log(params.name)
})事件参数
javascript
myChart.on("click", function (params) {
console.log("组件类型:", params.componentType)
console.log("系列类型:", params.seriesType)
console.log("系列索引:", params.seriesIndex)
console.log("系列名称:", params.seriesName)
console.log("数据名称:", params.name)
console.log("数据索引:", params.dataIndex)
console.log("数据值:", params.value)
console.log("颜色:", params.color)
})使用 query 过滤事件
javascript
// 只对特定系列响应
chart.on("click", "series", function () {})
chart.on("click", "series.line", function () {})
// 只对特定数据项响应
chart.on("click", { seriesName: "uuu" }, function () {})
chart.on("click", { seriesIndex: 1, name: "xx" }, function () {})
chart.on("click", { dataType: "node" }, function () {})组件交互事件
组件交互行为会触发相应的事件:
javascript
// 图例开关事件
myChart.on("legendselectchanged", function (params) {
var isSelected = params.selected[params.name]
console.log((isSelected ? "选中了" : "取消选中了") + "图例" + params.name)
})
// 数据区域缩放事件
myChart.on("datazoom", function (params) {
console.log("数据范围:", params.start, "-", params.end)
})监听空白处事件
javascript
myChart.getZr().on("click", function (event) {
if (!event.target) {
console.log("点击了空白处")
}
})触发图表行为
通过 dispatchAction 可以在程序中触发图表行为。
显示提示框
javascript
myChart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: 1
})高亮数据项
javascript
myChart.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex: 1
})
myChart.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: 1
})数据区域缩放
javascript
myChart.dispatchAction({
type: "dataZoom",
start: 20,
end: 30
})
// 批量操作
myChart.dispatchAction({
type: "dataZoom",
batch: [
{ start: 20, end: 30 },
{ dataZoomIndex: 1, start: 10, end: 20 }
]
})轮流高亮示例
javascript
var currentIndex = -1
setInterval(function () {
var dataLen = option.series[0].data.length
myChart.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: currentIndex
})
currentIndex = (currentIndex + 1) % dataLen
myChart.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex: currentIndex
})
myChart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: currentIndex
})
}, 1000)异步数据加载
很多时候数据需要异步加载后再填入。
异步加载示例
javascript
var myChart = echarts.init(document.getElementById("main"))
$.get("data.json").done(function (data) {
myChart.setOption({
title: {
text: "异步数据加载示例"
},
tooltip: {},
legend: {},
xAxis: {
data: data.categories
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: data.values
}
]
})
})先设置空坐标轴
javascript
var myChart = echarts.init(document.getElementById("main"))
myChart.setOption({
title: {
text: "异步数据加载示例"
},
tooltip: {},
legend: {
data: ["销量"]
},
xAxis: {
data: []
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: []
}
]
})
$.get("data.json").done(function (data) {
myChart.setOption({
xAxis: {
data: data.categories
},
series: [
{
name: "销量",
data: data.data
}
]
})
})Loading 动画
javascript
myChart.showLoading()
$.get("data.json").done(function (data) {
myChart.hideLoading()
myChart.setOption({
// ...
})
})数据的动态更新
ECharts 由数据驱动,数据的改变驱动图表展现的改变。
动态更新示例
javascript
var myChart = echarts.init(document.getElementById("main"))
setInterval(function () {
var data = []
for (var i = 0; i < 5; ++i) {
data.push(Math.round(Math.random() * 200))
}
myChart.setOption({
series: [
{
type: "bar",
data: data
}
]
})
}, 3000)setOption 合并模式
javascript
// 普通合并(默认)
chart.setOption(option)
// 不合并,完全替换
chart.setOption(option, true)
// 替换合并,指定组件类型
chart.setOption(option, {
replaceMerge: ["xAxis", "yAxis", "series"]
})数据转换
ECharts 5 开始支持数据转换功能,可以对数据进行过滤、排序、聚合等操作。
使用 transform
javascript
option = {
dataset: [
{
source: [
["product", "2015", "2016", "2017"],
["Matcha Latte", 43.3, 85.8, 93.7],
["Milk Tea", 83.1, 73.4, 55.1]
]
},
{
transform: {
type: "filter",
config: { dimension: "2015", gt: 50 }
}
}
],
series: [
{
type: "bar",
datasetIndex: 1
}
]
}内置转换类型
filter- 过滤数据sort- 排序aggregate- 聚合
过滤示例
javascript
{
transform: {
type: 'filter',
config: { dimension: 'age', gt: 18 }
}
}排序示例
javascript
{
transform: {
type: 'sort',
config: { dimension: 'score', order: 'desc' }
}
}聚合示例
javascript
{
transform: {
type: 'aggregate',
config: {
resultDimensions: [
{ name: 'total', from: 'sales', method: 'sum' },
{ name: 'avg', from: 'sales', method: 'average' },
{ name: 'max', from: 'sales', method: 'max' }
],
groupBy: 'category' // 按类别分组
}
}
}数据处理最佳实践
数据格式化
javascript
option = {
tooltip: {
formatter: function(params) {
return `${params.name}: ${params.value.toLocaleString()}`
}
},
xAxis: {
axisLabel: {
formatter: function(value) {
// 大数字格式化
if (value >= 10000) {
return (value / 10000).toFixed(1) + '万'
}
return value
}
}
},
series: [{
label: {
formatter: function(params) {
return `${params.name}\n${(params.value / 1000).toFixed(1)}K`
}
}
}]
}数据预处理
在传入 ECharts 前对数据进行预处理:
javascript
// 数据清洗
function cleanData(data) {
return data
.filter(item => item.value !== null && item.value !== undefined)
.map(item => ({
...item,
value: Number(item.value) || 0
}))
}
// 数据聚合
function aggregateData(data, groupBy, valueKey) {
const result = {}
data.forEach(item => {
const key = item[groupBy]
if (!result[key]) {
result[key] = 0
}
result[key] += item[valueKey]
})
return Object.entries(result).map(([name, value]) => ({ name, value }))
}
// 使用
const cleanedData = cleanData(rawData)
const aggregatedData = aggregateData(cleanedData, 'category', 'sales')大数据量处理
javascript
option = {
series: [{
type: 'scatter',
large: true, // 开启大数据优化
largeThreshold: 2000, // 数据量阈值
progressive: 200, // 渐进式渲染
progressiveThreshold: 3000, // 渐进式渲染阈值
data: largeDataArray
}]
}数据缓存策略
javascript
// 使用 Map 缓存处理后的数据
const dataCache = new Map()
function getCachedData(key, fetchFn) {
if (dataCache.has(key)) {
return Promise.resolve(dataCache.get(key))
}
return fetchFn().then(data => {
dataCache.set(key, data)
return data
})
}
// 使用
getCachedData('sales-2024', () => fetch('/api/sales/2024'))
.then(data => chart.setOption({ series: [{ data }] }))防抖与节流
频繁数据更新时使用防抖/节流:
javascript
import { debounce, throttle } from 'lodash'
// 防抖:停止操作后才更新
const updateChartDebounced = debounce((data) => {
chart.setOption({ series: [{ data }] })
}, 300)
// 节流:固定间隔更新
const updateChartThrottled = throttle((data) => {
chart.setOption({ series: [{ data }] })
}, 300)
// 实时数据场景使用节流
ws.onmessage = (e) => {
updateChartThrottled(JSON.parse(e.data))
}数据验证
javascript
function validateOption(option) {
const errors = []
// 检查必需字段
if (!option.series || option.series.length === 0) {
errors.push('series is required')
}
// 检查数据有效性
option.series?.forEach((s, i) => {
if (!Array.isArray(s.data)) {
errors.push(`series[${i}].data must be an array`)
}
})
if (errors.length > 0) {
console.warn('Option validation errors:', errors)
return false
}
return true
}
// 使用
const option = generateOption()
if (validateOption(option)) {
chart.setOption(option)
}高级交互模式
拖拽交互
javascript
option = {
series: [{
type: 'graph',
roam: true,
draggable: true, // 开启拖拽
data: nodes,
links: links
}]
}
// 监听拖拽事件
chart.on('graphRoam', function(params) {
console.log('图表被拖拽或缩放')
})刷选交互
javascript
option = {
toolbox: {
feature: {
brush: {
type: ['rect', 'polygon', 'lineX', 'lineY']
}
}
},
brush: {
toolbox: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'],
xAxisIndex: 0
},
series: [{
type: 'scatter',
data: scatterData
}]
}
// 监听刷选事件
chart.on('brushSelected', function(params) {
const selectedData = params.batch[0].selected[0].dataIndex
console.log('选中的数据索引:', selectedData)
})联动交互
多个图表之间的联动:
javascript
// 创建多个图表
const chart1 = echarts.init(dom1)
const chart2 = echarts.init(dom2)
// 设置联动
echarts.connect([chart1, chart2])
// 或使用 group
chart1.group = 'group1'
chart2.group = 'group1'
echarts.connect('group1')
// 一个图表的缩放、平移会同步到另一个图表自定义交互提示
javascript
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
},
formatter: function(params) {
let result = `<div style="font-weight:bold">${params[0].axisValue}</div>`
params.forEach(param => {
result += `
<div style="display:flex;align-items:center;margin:4px 0">
<span style="display:inline-block;width:10px;height:10px;background:${param.color};margin-right:8px"></span>
<span>${param.seriesName}: </span>
<span style="font-weight:bold;margin-left:auto">${param.value.toLocaleString()}</span>
</div>`
})
return result
}
}
}