Skip to content

Instantly share code, notes, and snippets.

@yan130
Created February 22, 2018 00:14
Show Gist options
  • Save yan130/2e1b8bd9e4f2028f1982dd20358614fe to your computer and use it in GitHub Desktop.
Save yan130/2e1b8bd9e4f2028f1982dd20358614fe to your computer and use it in GitHub Desktop.
react line chart with deviation
/*
* @flow
*/
import React, { Component } from 'react';
import d3 from 'd3';
import { Xaxis, Yaxis, Chart, Svg } from 'react-d3-core';
class LineChartWithDeviations extends Component {
constructor(props: Object) {
super(props);
this.state={
xline: d3.scale.linear(),
yline: d3.scale.linear()
}
}
componentWillReceiveProps(nextProps) {
const {
width,
height,
margins,
chartData
} = nextProps;
let xRange = [0, width - margins.left - margins.right];
let yRange = [height - margins.top - margins.bottom, 0];
let xDomain = d3.extent(chartData, function(d){ return d.x ; });
let yMax = d3.extent(chartData, function(d){ return d.y + d.d; })[1];
let yMin = d3.extent(chartData, function(d){ return d.y - d.d; })[0];
let yDomain = [yMin, yMax];
this.setState({xRange, xDomain, yRange, yDomain});
this.setState({xline : d3.scale.linear().domain(xDomain).range(xRange)});
this.setState({yline : d3.scale.linear().domain(yDomain).range(yRange)});
this.setState({barLength: 0.3*(xRange[1]/chartData.length)});
let that = this;
d3.select(this.refs.svgContainer).on('mousemove', function mouseMoveHandler() {
// console.log(d3.mouse(this));
let bisectDate = d3.bisector(function(d) { return d.x; }).left;
let x0 = that.state.xline.invert(d3.mouse(this)[0]- margins.left),
i = bisectDate(chartData, x0, 1) - 1,
d0 = chartData[i - 1],
d1 = chartData[i];
// for debug
// console.log(i)
let d = (d1 !== undefined || x0 - d0.x > d1.x - x0) ? d1 : d0;
if(d) {
that.setState({dFocus: d});
that.setState({xFocus: that.state.xline(d.x)});
that.setState({yFocus: that.state.yline(d.y)});
let text = d3.select("text");
let bbox = text.node().getBBox();
let padding = 2;
let rect = d3.select("rect")
.attr("width", bbox.width + (padding * 2))
.attr("height", bbox.height + (padding * 2));
}
})
}
_mkLine() {
let that = this;
return (
<path
stroke="black"
strokeWidth="2"
fill="none"
d={that._setAxes(this.props.chartData)}
strokeDasharray="5,5"
/>
)
}
_setAxes (data) {
let that = this;
let line = d3.svg.line()
.x(function(d) { return that.state.xline(d.x); })
.y(function(d) { return that.state.yline(d.y); })
return line(data);
}
_setDeviations (x, center, dev) {
let devData= [{"x": x, "y": center + dev}, {"x": x, "y": center - dev}]
return this._setAxes(devData)
}
_mkMarker() {
let that = this;
let {barLength} = this.state;
return (
<g>
{
this.props.chartData.map((line, i) => {
return (
<g key={i}>
<path stroke="black"
strokeWidth={1}
fill="none"
d={that._setDeviations(line.x, line.y, line.d)}/>
<line stroke="black"
strokeWidth={1}
fill="none"
x1={that.state.xline(line.x) - barLength} y1={this.state.yline(line.y + line.d)}
x2={that.state.xline(line.x) + barLength} y2={this.state.yline(line.y + line.d)}
/>
<line stroke="black"
strokeWidth={1}
fill="none"
x1={that.state.xline(line.x) - barLength} y1={this.state.yline(line.y - line.d)}
x2={that.state.xline(line.x) + barLength} y2={this.state.yline(line.y - line.d)}
/>
</g>
)
})
}
</g>
)
}
_mkFocus() {
const {xFocus, yFocus, dFocus, barLength} = this.state;
return(
dFocus ?
<g>
<circle
stroke="black"
strokeWidth="2"
fill="none"
cx={xFocus}
cy={yFocus}
r={4}
display={focus}/>
<rect x={xFocus} y={yFocus} rx={10} ry={10} width={150} height={50}
fill="rgb(31, 119, 180)"
stroke="rgb(31, 119, 180)"
opacity={0.8}
/>
<text x={xFocus } y={yFocus + barLength} fill="black">
<tspan x={xFocus } dy=".6em">{this.props.xLabel + ": " + dFocus.x}</tspan>
<tspan x={xFocus } dy="1.2em">{this.props.yLabel + ": " + dFocus.y.toFixed(2)}</tspan>
<tspan x={xFocus } dy="1.2em">{ "Deviation: " + dFocus.d.toFixed(2)}</tspan>
</text>
</g> : null
)
}
render() {
const {chartData, width, margins, height, id} = this.props;
let x = function(d) {
return d.x;
};
let y = function(d) {
return d.y;
};
let that = this;
let divStyle = {
width: width
};
let t = `translate(${margins.left}, ${margins.top})`;
let tX = `translate(0, ${height - margins.top})`;
return(
<div style={divStyle}>
<svg
height = {height}
width = {width}
id = {id}
ref = "svgContainer"
>
<g
transform = {t}
>
<g>
{this._mkFocus()}
</g>
<g>
{this._mkLine()}
</g>
<g>
{this._mkMarker()}
</g>
<g transform={`translate(0, ${height - margins.top - margins.bottom})`}>
<line x1={0} y1={0}
x2={width - margins.left - margins.right} y2={0}
stroke="black"
strokeWidth={1}
fill="none"/>
</g>
<g>
<line x1={0} y1={0}
x2={0} y2={height - margins.top - margins.bottom}
stroke="black"
strokeWidth={1}
fill="none"/>
</g>
<Xaxis
{...this.props}
xDomain= {this.state.xDomain}
xRange = {this.state.xRange}
x= {x}
/>
<Yaxis
{...this.props}
y= {y}
yDomain= {this.state.yDomain}
yRange = {this.state.yRange}
yAxisClassName = 'y-axis'
/>
</g>
</svg>
</div>
)
}
}
export default LineChartWithDeviations;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment