React项目中使用HighCharts
大家都知道BizCharts是基于react封装的一套图表工具,而HighCharts是基于jQuery的。但是由于本人对BizCharts甚是不熟,所以在react项目开发中选择了HighCharts,在使用及对接数据的时候也是遇到了各种问题。
下面简单说下项目需求:首先是两个网络数据源,要求随时间的变化网络折线图不断更新,且当展示一定的数据点后开始折线图从右往左平移。
下面附上该组件所有代码。
import React, { Component } from 'react' import PropTypes from 'prop-types' import assign from 'object-assign' import { autorun } from 'mobx' import { observer } from 'mobx-react' import Highcharts from 'highcharts' import _ from 'lodash' import ClassNames from 'classnames' import './index.less' @observer class NetworkStatus extends Component { static propTypes = { style: PropTypes.object, onClose: PropTypes.func, localPings: PropTypes.array, fullPings: PropTypes.array, } static defaultProps = { style: {}, onClose: _.noop, localPings: [], fullPings: [], } state = { stopFullPing: false, avgFullPingDelay: 0, lastFullPingDelay: 0, avgLocalPingDelay: 0, lastLocalPingDelay: 0, } componentDidMount() { const newChart = Highcharts.chart('chart_wrap', { chart: { type: 'spline', animation: false, marginRight: 10, }, // 自定义数据列颜色,默认从第一个数据列起调用第一个颜色代码,依次往后。 colors: ['#f08500', '#9dc239'], // 不显示右下角的版权信息 credits: { enabled: false, }, plotOptions: { series: { // 去掉鼠标悬浮到曲线上的默认线条加粗效果 states: { hover: { enabled: false, }, }, // 禁用图例点击事件 events: { legendItemClick(e) { return false }, }, }, }, // 是否使用国际标准时间 time: { useUTC: false, }, // 不显示头部标题 title: { text: null, }, xAxis: { type: 'datetime', // tickPixelInterval: 150, tickInterval: 60000, // labels: { // step: 60, // }, }, yAxis: { title: { text: null, }, }, // 修改图例样式 legend: { enabled: true, itemStyle: { color: '#4a4a4a', fontSize: '12px', fontWeight: 'normal' }, itemHoverStyle: { cursor: 'normal' }, }, // 不显示导出按钮 exporting: { enabled: false, }, // 数据列具体数据设置 series: [{ // 表示使用哪个y轴 yAxis: 0, name: 'Full Ping Delays', lineWidth: 1, // 不显示曲线上的实心圆点 marker: { enabled: false, }, data: [], }, { yAxis: 0, name: 'Ping Delays', lineWidth: 1, // 不显示曲线上的实心圆点 marker: { enabled: false, }, data: [], }], }) this.chart = newChart this.initChartData(this.props.fullPings, this.props.localPings) // store中存储的网络数据每次更新时触发以下代码的执行,即动态往折线图中添加点。 this.cancelAutoRuns = [ autorun(() => { if (!_.isEmpty(this.props.fullPings)) { const fullPingsArrayLength = this.props.fullPings.length let totalFullPingDelay = 0 _.forEach(this.props.fullPings, (pings) => { totalFullPingDelay += pings.delay }) this.setState({ avgFullPingDelay: Math.round(totalFullPingDelay / fullPingsArrayLength), lastFullPingDelay: this.props.fullPings[fullPingsArrayLength - 1].delay, }) const x = this.props.fullPings[fullPingsArrayLength - 1].time const y = this.props.fullPings[fullPingsArrayLength - 1].delay const seriesData = this.chart.series[0].data const shift = seriesData.length > 200 if (x !== seriesData[seriesData.length - 1].x) { this.chart.series[0].addPoint([x, y], false, shift, false) } this.chart.redraw() } }), autorun(() => { if (!_.isEmpty(this.props.localPings)) { const localPingsArrayLength = this.props.localPings.length let totalLocalPingDelay = 0 _.forEach(this.props.localPings, (pings) => { totalLocalPingDelay += pings.delay }) this.setState({ avgLocalPingDelay: Math.round(totalLocalPingDelay / localPingsArrayLength), lastLocalPingDelay: this.props.localPings[localPingsArrayLength - 1].delay, }) const x = this.props.localPings[localPingsArrayLength - 1].time const y = this.props.localPings[localPingsArrayLength - 1].delay const seriesData = this.chart.series[1].data const shift = seriesData.length > 200 if (x !== seriesData[seriesData.length - 1].x) { this.chart.series[1].addPoint([x, y], false, shift, false) } this.chart.redraw() } }), ] } componentWillUnmount() { this.chart.destroy() _.forEach(this.cancelAutoRuns, f => f()) } chart = null cancelAutoRuns = null // 初始化localPings和fullPings折线图数据 initChartData = (fullPings, localPings) => { if (_.isEmpty(fullPings) || _.isEmpty(localPings)) { return } const fullPingsArrayLength = fullPings.length const localPingsArrayLength = localPings.length let totalFullPingDelay = 0 let totalLocalPingDelay = 0 // 初始化数据时,当store中存储的网络数据少于折线图中定义的展示的点的个数(200)时就直接循环store中所有的数据,当store中数据大于点个数时,取store中后200个数据进行展示 if (fullPingsArrayLength > 200 && localPingsArrayLength > 200) { const newFullPings = fullPings.slice(-200) const newLocalPings = localPings.slice(-200) this.cyclicPingsData(newFullPings, newLocalPings) } else { this.cyclicPingsData(fullPings, localPings) } _.forEach(fullPings, (pings) => { totalFullPingDelay += pings.delay }) _.forEach(localPings, (pings) => { totalLocalPingDelay += pings.delay }) this.setState({ avgFullPingDelay: Math.round(totalFullPingDelay / fullPingsArrayLength), lastFullPingDelay: fullPings[fullPingsArrayLength - 1].delay, avgLocalPingDelay: Math.round(totalLocalPingDelay / localPingsArrayLength), lastLocalPingDelay: localPings[localPingsArrayLength - 1].delay, }) } cyclicPingsData = (fullPings, localPings) => { _.forEach(fullPings, (pings) => { const x = pings.time const y = pings.delay this.chart.series[0].addPoint([x, y], false, false, true) }) _.forEach(localPings, (pings) => { const x = pings.time const y = pings.delay this.chart.series[1].addPoint([x, y], false, false, true) }) this.chart.redraw() } handleClickCloseBtn = () => { this.props.onClose() } handleClickChangeBtn = () => { const { stopFullPing } = this.state this.setState({ stopFullPing: !stopFullPing }) // 点击按钮的同时隐藏或显示 Full Ping Delays 折线 if (!this.state.stopFullPing) { this.chart.series[0].hide() } else { this.chart.series[0].show() } } render() { const wrapStyles = assign({}, this.props.style) const hideFullPingTextClasses = ClassNames({ hide: this.state.stopFullPing === true, }) return ( <div className="network_status_component_wrap" style={wrapStyles}> <div className="header"> <span className="exit_icon" onClick={this.handleClickCloseBtn} role="button" tabIndex="0" /> </div> <div className="container_wrap"> <div className="text_information_wrap"> <p className="text_one_p"> <span>server: <span className="black_color">47.94.115.241:501</span></span> <span className="button_span" onClick={this.handleClickChangeBtn} role="button" tabIndex="0" > { this.state.stopFullPing ? 'start full ping' : 'stop full ping' } </span> <span className="black_color">124.204.55.50 中国 北京 鹏博士/联通</span> </p> <p id="text_two_p" className={hideFullPingTextClasses}> <span className="margin_right">avg full ping delay: <span>{this.state.avgFullPingDelay}</span> ms</span> <span>last full ping delay: <span>{this.state.lastFullPingDelay}</span> ms</span> </p> <p className="text_three_p"> <span className="margin_right">avg ping delay: <span>{this.state.avgLocalPingDelay}</span> ms</span> <span>last ping delay: <span>{this.state.lastLocalPingDelay}</span> ms</span> </p> </div> <div id="chart_wrap" /> </div> </div> ) } } export default NetworkStatus
如下是效果图: