React と D3 ver.4 組み合わせて使うサンプル。
まだ、試行錯誤中。
Made with blockup-react
React と D3 ver.4 組み合わせて使うサンプル。
まだ、試行錯誤中。
Made with blockup-react
//todo:後で分割する。 | |
import * as d3 from "d3"; | |
import React, { Component } from "react"; | |
export default function D3blackbox(d3render) { | |
return class Blackbox extends Component { | |
componentDidMount() { | |
d3render.call(this); | |
} | |
componentDidUpdate() { | |
d3render.call(this); | |
} | |
render() { | |
const transform = this.props.transform || ""; | |
return <g transform={transform} ref="anchor" />; | |
} | |
}; | |
} | |
export const XAxis = D3blackbox(function() { | |
const axis = d3 | |
.axisBottom() | |
.tickFormat(d => d3.timeFormat("%H:%M %S")(d)) | |
.scale(this.props.xScale); | |
d3 | |
.select(this.refs.anchor) | |
.classed("xAxis", true) | |
.transition() | |
.call(axis); | |
}); | |
export const YAxis = D3blackbox(function() { | |
const axis = d3 | |
.axisLeft() | |
.tickFormat(d => d) | |
.scale(this.props.yScale); | |
d3 | |
.select(this.refs.anchor) | |
.classed("yAxis", true) | |
.transition() | |
.call(axis); | |
}); | |
export const YGrid = D3blackbox(function() { | |
const axis = d3 | |
.axisRight() | |
.tickFormat(d => null) | |
.scale(this.props.yScale) | |
.tickSizeOuter(0) | |
.tickSizeInner(this.props.plotWidth); | |
d3 | |
.select(this.refs.anchor) | |
.classed("yGrid", true) | |
.call(axis); | |
}); | |
export const Line = D3blackbox(function() { | |
const path = d3 | |
.line() | |
.x(d => d.x) | |
.y(d => d.y); | |
const parent = d3.select(this.refs.anchor); | |
const current = parent.selectAll(".valueLine").data([this.props.plotData]); | |
current.interrupt(); | |
const enter = current | |
.enter() | |
.append("path") | |
.classed("valueLine", true); | |
const valueLine = current.merge(enter); | |
current | |
.transition() | |
.attr("transform", `translate(${this.props.xSlide}, 0)`) | |
.on("end", () => { | |
valueLine.attr("d", path); | |
current.attr("transform", null); | |
}); | |
}); |
import * as d3 from "d3"; | |
/* | |
export const loadAllData = (callback = () => {} ) => { | |
const q = d3.queue() | |
q.defer(d3.tsv, './data.tsv', cast) | |
q.await((error, data) =>{ | |
callback(data); | |
}) | |
} | |
*/ | |
//デモ用ランダムデータ生成function | |
const MakeData = () => { | |
const rnd = d3.randomNormal(180, 20); | |
let data = d3 | |
.range(300) | |
.map(i => { | |
return { date: new Date(new Date().getTime() - 1000 * i), value: rnd() }; | |
}) | |
.reverse(); | |
return () => { | |
data.push({ date: new Date(), value: rnd() }); | |
data.shift(); | |
return data; | |
}; | |
}; | |
const md = MakeData(); | |
export const loadAllData = (callback = () => {}) => { | |
callback(md()); | |
}; |
body,html{width:100%;heigth:100%;padding:0x;margin:0}.axisLaeyr .xAxis .domain,.axisLaeyr .xAxis .tick>line{display:none}.axisLaeyr .yAxis .domain,.axisLaeyr .yAxis .tick>line{display:none}.axisLaeyr .yGrid .domain{display:none}.axisLaeyr .yGrid .tick>line{stroke-dasharray:3}.plotLayer .valueLine{fill:none;stroke-width:1.5;stroke:#00f}.button{width:100%}.button button{display:block;width:100px;height:50px;margin:0 auto} |
import React, { Component } from "react"; | |
import * as d3 from "d3"; | |
import { XAxis, YAxis, YGrid, Line } from "./ChartComponents.js"; | |
class LineChart extends Component { | |
constructor(props) { | |
super(); | |
} | |
updateScale(props) { | |
const data = props.data; | |
const xScale = d3.scaleTime(); | |
const yScale = d3.scaleLinear().nice(); | |
const xDomain = d3.extent(data, d => d.date); | |
const yDomain = props.yDomain || [0, d3.max(data, d => props.yFn(d))]; | |
xScale | |
.domain(xDomain) | |
.range([0, props.width - (props.margin.left + props.margin.right)]); | |
yScale | |
.domain(yDomain) | |
.range([props.height - (props.margin.top + props.margin.bottom), 0]); | |
return {xScale, yScale} | |
} | |
updatePlotSize(props){ | |
const plotWidth = | |
props.width - (props.margin.left + props.margin.right); | |
const plotHeight = | |
props.height - (props.margin.top + props.margin.bottom); | |
return {plotWidth, plotHeight} | |
} | |
render() { | |
const {xScale, yScale} = this.updateScale(this.props); | |
const {plotWidth, plotHeight} = this.updatePlotSize(this.props); | |
const metaData = { | |
xScale: xScale, | |
yScale: yScale, | |
plotWidth: plotWidth, | |
plotHeight: plotHeight, | |
xSlide: -xScale(this.props.xFn(this.props.data[1])) | |
}; | |
const plotData = { | |
plotData: this.props.data.map((d, i) => { | |
return { | |
id: i, | |
data: d, | |
x: xScale(this.props.xFn(d)), | |
y: yScale(this.props.yFn(d)) | |
}; | |
}) | |
}; | |
return ( | |
<svg width={this.props.width} height={this.props.height}> | |
<g | |
className="axisLaeyr" | |
width={plotWidth} | |
height={plotHeight} | |
transform={`translate(${this.props.margin.left}, ${this.props.margin | |
.top})`} | |
> | |
<YGrid {...metaData} /> | |
<XAxis {...metaData} transform={`translate(0,${plotHeight})`} /> | |
<YAxis {...metaData} /> | |
</g> | |
<g | |
className="plotLayer" | |
width={plotWidth} | |
height={plotHeight} | |
transform={`translate(${this.props.margin.left}, ${this.props.margin | |
.top})`} | |
> | |
<Line {...metaData} {...plotData} /> | |
</g> | |
</svg> | |
); | |
} | |
} | |
export default LineChart; |
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)