大家都知道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
如下是效果圖:

