最近一朋友让我帮他弄一个股票图。了解我的人都知道很长一段时间我的工作就是画图表,Flex 之后我用 Flotr2 开发过图表,但这个插件不支持 K 线图。我在网上找了找,发现 Echart 的 K 线图很好用也支持 K 线图。考虑到以后可能会复用,就结合 React 封装了一个 K 线组件。
基本上是在官网实例 上封装的。组件的封装其实就是确定输入输出接口的过程,朋友的需求比较简单,不涉及组件的数据输出,只需要传入 dataset
(图表数据源)和 seriesName
(K 线系列名称)两个属性就可以了,我还加了一个 option
属性,设置它可以完全覆盖组件内部的默认设置,这样复用性就相对高一些。
一、属性定义如下
StockChart.propTypes = {
dataset: PropTypes.arrayOf(PropTypes.array).isRequired,
option: PropTypes.object,
seriesName: PropTypes.string,
};
注意:这里的合并对象层级很深,要用深拷贝。考虑到时兼容必问题,我们使用 lodash 的 merge
函数来实现。
二、按需引入组件
考虑到 Echart 整体上是很重的,我选择发按需引入组件:
import 'echarts/lib/chart/line';
import 'echarts/lib/chart/candlestick';
import 'echarts/lib/component/gridSimple';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/legend';
import 'echarts/lib/component/dataZoom';
三、自定义
原实例的的工具提示不是中文的,显示顺序不符合国人的习惯,也没有涨跌、成交、振幅这些指标,所以自定义如下:
formatter: ([param, ...tail]) => {
const data = param.data;
const float = data[4] - data[3];
return param.seriesName +
'<br/>' + param.name +
'<br/>开盘:' + data[1] +
'<br/>最高:' + data[4] +
'<br/>最低:' + data[3] +
'<br/>收盘:' + data[2] +
'<br/>涨跌:' + data[5] + '(' + data[6] + ')' +
'<br/>成交:' + (+data[7] / 100000000).toFixed(2) + '亿手' +
'<br/>振幅:' + float.toFixed(2) + '(' + (float / data[4] * 100).toFixed(2) + '%)' +
'<br/>' + tail.map(item => item.seriesName + ':' + item.value).join('<br/>')
}
四、图例系列解耦
原实例的图例数据和系列名称存在耦合,我处理了一下:
getMas(ranges) {
return ranges.map(item => {
return {
name: 'MA' + String(item),
type: 'line',
data: this.calculateMA(item),
smooth: true,
showSymbol: false,
lineStyle: {
width: 1
}
}
})
}
get series() {
const arr = this.getMas([5, 10, 20, 30])
return [
{
type: 'candlestick',
name: this.props.seriesName,
data: this.data,
itemStyle: {
normal: {
color: '#FD1050',
color0: '#0CF49B',
borderColor: '#FD1050',
borderColor0: '#0CF49B'
}
}
},
...arr
]
}
五、自适应处理
原实例的图表自适应代码写在父窗口上了,此处我们要在组件中实现这一功能,需要处理一下:
componentDidMount() {
// 初始化图表
this.chart = echarts.init(this.el, this.props.theme);
// 将传入的配置(包含数据 ) 注入
this.setOption();
// 监听屏幕缩放,重新绘制 echart 图表
window.addEventListener('resize', throttle(() => {
// 减少回流提高性能
this.resize()
}, 100));
}
componentWillUnmount() {
// 组件卸载前卸载图表
window.removeEventListener('resize', this.resize)
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
}
注意:我们是在 componentDidMount 中监听 window 的 resize 事件,这个事件浏览器无法自动进行垃圾回收,有可能导致内存泄露,因此需要我们在 componentWillUnmount 中手动销毁。另外,resize 是个高频事件,我们使用 lodash 的 throttle 函数来做节流操作,减少回流提高性能。
示例资源
访问 codesandbox 查看完整项目代码及最终效果。
?Happy coding!
评论 (0)