摘要
本文最适合平时不怎么接触echarts图表,但是偶尔有一些简单图表需求或者没有接触过echarts的 jym 食用,可以帮助你在项目中快速接入echarts,不用看密密麻麻的文档,就可以快速复原ui效果了,大大提升开发效率。
背景
最近两月公司比较忙,很久了没有总结了,这个月有个关于图表的需求,由于之前接过echarts感觉应该很快就能写完,但是由于距离上次接触有段时间了,很多都忘记了,排期时排少了,只能狠狠加班了。
echarts直接使用还是很简单,复杂的点在于ui的复原,复原ui就需要看着那密密麻麻的文档,这里我就打算写一个当前项目专用的hook,因为一般一个项目下图表风格都是一样的,不会有什么变动;
最后整整花了一天的时间成功封装了一个hook,这样以后再有折线图需求就可以快速实现,即使未来有其他项目也需要图表,我也可以进行二次改动快速实现新的ui,最为新项目的hook。
左边ui,右边实现,复原度也高达95%了
在组件中如何使用
我期望这个hook的使用是这样的,通过参数传数据,导出一个ref,绑定到一个需要渲染的div上,数据变化div重新渲染。图表的样式有hook内置好,
const { chartRef } = useLineEcharts({
//数据
})
<div ref={chartRef} />
代码片段
点击运行查看实际效果
//关键代码
const chartOption = useMemo(() => {
return {
titleText: chartData?.indicatorsDesc,
xAxisData: chartData?.time,
seriesData: [
{
name: '今日',
data: chartData?.today,
lineColor: '#3D70FF',
},
{
name: '昨日',
data: chartData?.yesterday,
lineColor: '#42C4D8',
},
{
name: '7天前',
data: chartData?.sevenDaysAgo,
lineColor: '#C1C1C1',
lineType: 'dashed' as const,
},
],
Tooltip: {
titleFormatter: formatterTitle,
valueFormatter: formatterVal,
},
};
}, [chartData]);
const { chartRef } = useLineEcharts(chartOption);
Hook 概览
- 职责:负责 ECharts 实例的初始化、设置 option、销毁,暴露 chartRef 以供绑定 DOM。
- tooltip的属性中是不支持设置background属性的,只能设置backgroundColor,但是在css中backgroundColor是不能设置渐变色的,这里我利用了一个bug来实现的(也是偶然发现的)。
iimport { useEffect, useRef } from 'react';
import * as echarts from 'echarts';
// https://echarts.apache.org/zh/option.html#title
interface UseLineEchartsProps {
/**
* 图表标题文本
*/
titleText?: string;
/**
* x轴数据
*/
xAxisData: string[];
/**
* 系列数据数组
* lineColor 系列线条颜色,若未指定则按默认颜色列表顺序取用
*/
seriesData: Array<{
name: string; // 系列名称
data: number[] | string[]; // 数据
lineColor?: string; // 线条颜色
lineType?: 'solid' | 'dashed'; // 线条类型 实线 | 虚线
yAxisIndex?: number; // 显示的y轴位置 0 左侧 1 右侧 默认0
}>;
/**
* 提示框格式化函数
*/
Tooltip?: {
// 标题格式化
titleFormatter?: (data: any) => string;
// 值格式化
valueFormatter?: (value: any) => string;
};
}
// 默认颜色列表
const DEFAULT_COLOR = [
'#3D70FF',
'#42C4D8',
'#C1C1C1',
'#8bcff1',
'#d393e6',
'#FF2842',
'#079933',
];
/**
* useLineEcharts
* 该hooks用于封装ECharts折线图的初始化与配置
*/
export const useLineEcharts = ({
titleText,
xAxisData,
seriesData,
Tooltip = {
titleFormatter: (data) => data,
valueFormatter: (value) => value,
},
}: UseLineEchartsProps) => {
// chartRef用于绑定ECharts实例的DOM节点
const chartRef = useRef<HTMLDivElement>(null);
const chartInstance = useRef<echarts.ECharts | null>(null);
useEffect(() => {
if (chartRef.current) {
// 初始化ECharts实例
chartInstance.current = echarts.init(chartRef.current);
// 组装每个系列的颜色,若未指定则按默认颜色列表顺序取用
const color: string[] = seriesData?.map(
(item, index) => item.lineColor || DEFAULT_COLOR[index],
);
// ECharts配置项
let option = {
// color: 图表全局调色盘,对应每个系列的颜色
color,
// 标题组件
title: {
text: titleText, // 标题文本
padding: [10, 0, 0, 0], // 标题内边距
textStyle: {
fontSize: 16,
},
},
// 网格组件,控制图表绘图区的位置
grid: {
left: 5,
right: 16,
bottom: 5,
containLabel: true, // 保证坐标轴标签不被裁剪
},
// 提示框组件
tooltip: {
trigger: 'axis', // 触发类型,坐标轴触发
axisPointer: {
type: 'line', // 指示器类型为直线
lineStyle: {
color: '#4275FF', // 指示线颜色
},
},
// 设置提示框背景色(利用小bug实现渐变背景)
backgroundColor:
'#fff;background:linear-gradient( #EBF1FF 7%, #ffffff 44%);',
// 提示框内容格式化
formatter: (data) => {
const _title = Tooltip?.titleFormatter?.(data[0].name);
const strArr = data.map((item) => {
const _val = Tooltip?.valueFormatter?.(item.value);
const isHasName = item.seriesName.length > 0;
const jcv = isHasName ? 'space-between' : 'start';
const mr = isHasName ? '10px' : '0px';
return `<div style="display:inline-flex; align-items: center; justify-content: ${jcv}; width: 100%;"><div>${item.marker}<div style="display: inline-block;margin-right: ${mr};"> ${item.seriesName}</div> </div><div style="font-weight: bold; text-align: right;">${_val}</div></div>`;
});
return `<div>${_title}<br/>${strArr.join('<br/>')}</div>`;
},
},
// 图例组件(右上角的提示)
legend: {
show: true, // 显示图例
top: 8, // 距离顶部8px
right: 0, // 靠右
itemWidth: 8, // 图例标记宽度
itemStyle: {
opacity: 0, // 图例标记透明(只显示文字)
},
textStyle: {
color: '#757A86', // 图例文字颜色
},
lineStyle: {
cap: 'round', // 图例线条端点样式
},
// 图例数据
data: seriesData.map((item) => ({
name: item.name,
lineStyle: {
type: item.lineType,
dashOffset: 6,
},
})),
},
// 区域缩放组件,支持内部缩放
dataZoom: [
{
type: 'inside', // 内置型缩放
start: 0, // 默认起始百分比
end: 100, // 默认结束百分比
},
],
// x轴配置
xAxis: {
type: 'category', // 类目轴
axisTick: {
show: false, // 不显示刻度线
},
offset: 0, // 轴线偏移‘
axisLine: {
lineStyle: {
color: '#F0F0F0', // 轴线颜色
},
},
axisLabel: {
color: '#616161', // 标签文字颜色
showMinLabel: true, // 显示最小标签
showMaxLabel: true, // 显示最大标签
},
splitLine: {
lineStyle: {
color: '#F0F0F0', // 分割线颜色
},
},
data: xAxisData, // x轴数据
},
// y轴配置
yAxis: [
{
offset: 5, // 轴线偏移
type: 'value', // 数值轴
position: 'left', // 轴线在左侧
axisLabel: {
color: '#616161', // 标签文字颜色
},
},
{
offset: 0, // 轴线偏移
type: 'value', // 数值轴
position: 'right', // 轴线在右侧
axisLabel: {
color: '#616161', // 标签文字颜色
},
},
],
// 折线图列表,每个对象对应一条折线
series: seriesData.map((item) => {
return {
type: 'line', // 折线图
name: item.name, // 系列名称
showSymbol: false, // 不显示拐点标记
smooth: true, // 平滑曲线
lineStyle: {
width: 1.5, // 线条宽度
type: item.lineType, // 线条类型
color: item.lineColor, // 线条颜色
},
yAxisIndex: item.yAxisIndex || 0,
data: item.data?.map((it) => Number(String(it).replace(/%+$/, ''))), // 数据
};
}),
};
// 设置ECharts配置项
chartInstance.current.setOption(option);
}
// 组件卸载时销毁ECharts实例,防止内存泄漏
return () => {
chartInstance.current?.dispose();
};
}, [xAxisData, seriesData, titleText, Tooltip]);
// 返回chartRef,需绑定到div元素
return {
chartRef,
};
};
最后
总之,就是对折线图进行一层ui上的封装,一来方便了项目下未来折线图的需求;二来以后其他项目在做图表就不需要看密密麻麻的文档,可以直接在这个hook上改动,可以大幅提高开发效率。