掘金数据中心上线也有好一阵子了,近来忙里偷闲,试着用 ECharts(5.4版本) 自己实现了如下所示的折线图,在此做个记录与分享。
初识 ECharts
ECharts 全称 Enterprise Charts,意为企业级数据图表,是一个百度团队开源的现在交由 Apache 基金会管理的基于 js 的可视化图表库,它的底层依赖的是 ZRender 图形库。
使用 echarts 制作图表大体分为 3 步:
- 定义一个用于展示图表的 DOM 容器,一定要定义高度,宽度可以省略,默认等于父级容器的宽度:
<div id="main" style="height: 400px"></div>
- 引入库文件,并通过
echarts.init
,传入第 1 步准备好的 DOM 容器,初始化一个 echarts 实例myChart
:
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script>
const myChart = echarts.init(document.getElementById('main'))
</script>
如果想生成深色模式的图表,可以给 init()
传入第 2 个参数 'dark'
;echarts 默认使用 canvas 绘图,如果想使用 svg 绘图,则需要传入第 3 个参数 { renderer: 'svg' }
:
const myChart = echarts.init(document.getElementById('main'), 'dark', {
renderer: 'svg'
})
- 通过实例的
setOption
方法传入图表的配置:
myChart.setOption(option)
echarts 上手体验很容易,但它的 option
的配置十分复杂,有诸多的可配置选项,下面将详细介绍如何配置 option
以实现我们想要的的折线图效果。
配置 option
基础配置
我们可以将一个图表内的不同部分看成不同的组件,比如一个折线图有 x 轴,有 y 轴等,而一个配置项就是对一个组件的定义。先看看生成一个基本的折线图,需要哪些配置:
// 代码片段 1
const option = {
xAxis: {
data: [
'23-10-04',
'23-10-05',
'23-10-06',
'23-10-07',
'23-10-08',
'23-10-09'
]
},
yAxis: {},
series: [
// 展现数
{
type: 'line',
data: [205, 201, 283, 561, 1098, 1046, 1134]
},
// 阅读数
{
type: 'line',
data: [68, 71, 85, 130, 280, 271, 319]
}
]
}
xAxis
:配置 x 轴。data
用于配置当 x 轴为类目轴时类目的数据,默认就是类目轴,即xAxis.type = 'category'
;yAxis
:配置 y 轴。看起来我们只是给yAxis
赋值了一个空对象,但这是必需的,因为其有个默认属性show
,值为true
,表示要显示 y 轴;series
:值为一个数组,数组里的每一项最终会映射成一个图形。比如我们想在一个坐标系中生成 2 个折线图,一个用于表示“展现数”,一个用于表示“阅读数”,就给数组添加如代码片段 1 所示的 2 个对象,对象中type: 'line'
表示是折线图,data
则表示具体数据。
代码片段 1 生成的图表如下:
可以看到初具雏形但与我们的预期效果还有很大差距,接下去我们一步步对各个组件做进一步地配置。
x 轴
对比掘金的内容数据图,发现有几点需要改进:
- x 轴轴线应该隐藏;
- x 轴中的分隔线不应该显示;
- 折线的起点与终点距离 x 轴两端还有一些空间,需要去除;
- 刻度标签的样式需要修改,具体为字体颜色要改得淡一些,与 x 轴的距离需要加长,并且第 1 个标签应该左对齐,最后一个标签右对齐,其余的中间对齐:
通过配置 xAxis
实现修改:
xAxis: {
axisLine: {
show: false // 隐藏轴线
},
axisTick: {
show: false // 隐藏分隔线
},
boundaryGap: false, // 坐标轴两边不留白
// 标签文字样式修改
axisLabel: {
color: '#9DA5AF',
margin: 12,
formatter: (value, index) => {
if (index === 0) return `{a|${value}}`
if (index === 6) return `{b|${value}}`
return value
},
rich: {
a: {
padding: [0, 0, 0, 40] // 左边距 40
},
b: {
padding: [0, 40, 0, 0] // 右边距 40
}
}
},
// ...
},
因为首尾标签的对齐方式和其它标签不同,所以使用 xAxis.axisLabel.formatter
对不同标签做了特殊处理。formatter
的值我这里采用了回调函数的形式,该函数在调用时会传入刻度类目(value
)和刻度的索引(index
)。{a|${value}}
写法代表的意思是对于 value
值,应用定义于 rich
的 a
的样式。注意,这里使用 padding
而不是 align
来修改标签的对齐方式,因为后者会无效,这在 echarts 的 github 的 issues 里有所提及:
现在效果如下:
y 轴
目前 y 轴需要修改的有 2 点:
- y 坐标轴在坐标系区域中的分隔线样式需要改为虚线;
- 标签距离过近:
通过配置 yAxis
实现修改。对于标签的修改同 x 轴一样,也是修改 axisLabel
属性;对于分隔线的修改则是通过 yAxis.splitLine.lineStyle
:
yAxis: {
splitLine: {
lineStyle: {
type: 'dashed',
color: ['#EDEEF1']
}
},
axisLabel: {
color: '#9DA5AF',
margin: 20
}
},
现在折线图如下所示:
折线样式
现在我们通过配置 series
来改变折线的样式,以“展现数”这条折线为例:
series: [
{
name: '展现数',
type: 'line',
smooth: true, // 平滑
data: [205, 201, 283, 561, 1098, 1046, 1134],
itemStyle: {
color: '#9F54FF' // 改变颜色
},
showSymbol: false, // 隐藏标记点
symbol: 'pin', // 当鼠标悬浮在折线图上出现 tooltip 时,显示的标记类型
symbolOffset: [0, '75%'], // 标记点的位置
// 区域填充
areaStyle: {
color: {
type: 'linear', // 线性渐变
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(173,102,255,0.1)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(173,102,255,0.01)' // 100% 处的颜色
}
]
}
}
},
]
name
属性在 tooltip 组件中会使用到;- 通过
smooth: true
让折线的线条变得平滑; - 通过
itemStyle.color
让线条以及标记点呈现指定的颜色; - 让线条的下部分区域有渐变填充效果则是通过
areaStyle.color
属性,x: 0, y: 0, x2: 0, y2: 1
表明线性渐变的方向是从上到下;
“阅读数”折线也经过对应调整后,折线图样式如下:
提示工具
现在我们通过配置 tooltip
来实现下图所示鼠标悬浮于图表时显示的提示工具:
tooltip.trigger
设置为 'axis'
表明触发显示提示工具的条件是坐标轴:
tooltip: {
trigger: 'axis',
}
这样展示的提示组件内部的默认样式,与预期效果稍有不同:
- 日期的字体没有加粗;
- “展现数”,“阅读数”后面没有冒号;
- 数量值字体不需要为粗体;
- 每行文字间的间隔比较小:
所以还需要使用 tooltip.formatter
对提示框浮层内容进行样式修改:
tooltip: {
// ...
formatter: params => {
const title = `<div style="font-weight: bold; margin-bottom: 10px">${params[0].axisValue}</div>`
let content = ''
params.forEach(item => {
content += `
<div style="margin-bottom: 6px">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}"></span>
<span>${item.seriesName}:${item.value}</span>
</div>
`
})
return title + content
}
}
formatter
的值使用回调函数,该回调的类型如下,从 params
中可以获取到需要的数据,返回值可以直接是 HTMLElement :
图例
接着我们来添加图例:
通过 legend
配置:
legend: {
bottom: 0,
itemGap: 30,
itemWidth: 20,
icon: 'path://M431.36 652.16L261.76 482.56a48 48 0 1 0-67.84 67.904l203.584 203.648a48 48 0 0 0 71.232-3.648l381.184-381.184a48 48 0 1 0-67.904-67.84l-350.72 350.72zM128 0h768a128 128 0 0 1 128 128v768a128 128 0 0 1-128 128H128a128 128 0 0 1-128-128V128a128 128 0 0 1 128-128z'
}
bottom: 0
让图例位于图表的下方,默认会在图表的上方;itemGap
设置图例每项之间的间隔;itemWidth
的设置是为了调整图例的 icon 和文字的间距;icon
用于设置图例中的图标,ECharts 提供的类型只有圆或矩形等,想用自定义图标,我通过使用'path://路径'
的方式设置,路径则是阿里图标库的“复制 SVG 代码”得到的:
绘制网格
最后我们来调整下绘制网格的配置,改变 grid 组件离容器左右两侧的距离:
grid: {
left: '5%',
right: '1%'
},
至于网格的概念,可参见介绍 canvas 的文章。
响应式
如果想让图表可以根据浏览器窗口的调整自动调整,可以添加个监听窗口缩放的事件,一旦事件触发则执行 resize
方法(为避免频繁触发,我使用了 lodash 的 _.throttle
进行了节流):
const resize = _.throttle(() => {
myChart.resize()
}, 1000)
window.addEventListener('resize', resize)