RealtimeChart = (props) => {
const [width, height] = props.size;
const xDomain = props.xDomain;
const margin = {left: 25, right: 50, bottom: 100, top: 30};
const xScale = d3.scaleTime()
.domain(xDomain)
.range([margin.left, width - margin.right])
.nice();
const yScale = d3.scaleLinear()
.domain(props.yDomain)
.range([height-margin.bottom, margin.top]);
const onUpdateXScale = xScale.copy();
const xAxis = (g, scale = xScale) => {
const drawAxis =
d3.axisBottom(scale)
.ticks(d3.timeSecond.every(1))
.tickSize(height-margin.bottom)
.tickFormat((domainValue) => {
const formatTime = d3.timeFormat('%I:%M:%S');
return formatTime(domainValue);
});
g.call(drawAxis)
}
const yAxis = (g, scale = yScale) => {
const drawAxis = d3.axisRight(scale)
.tickSize(width-margin.right);
g
.call(drawAxis)
.call(g => g.select(".domain").remove());
}
const lineFunc = d3.line()
.x(d => onUpdateXScale(d.time))
.y(d => yScale(d.value))
.curve(d3.curveCardinal);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("font-size", "0.625rem")
.attr("font-family", "sans-serif")
.attr("font-weight", "bold");
const xAxisG = svg
.append("g")
.attr("class", "xAxis")
.attr("transform", d => `translate(0, ${margin.top})`)
.call(xAxis, onUpdateXScale);
const yAxisG = svg
.append("g")
.attr("class", "yAxis")
.attr("transform", d => `translate(${margin.left}, 0)`)
.call(yAxis, yScale);
xAxisG
.selectAll("g.tick")
.select("line")
.attr("stroke", "white");
yAxisG
.selectAll("g.tick")
.select("line")
.attr("stroke", "#bdbdbd");
const renderData = (props) => {
const { data, data2 } = props;
xAxisG
.selectAll("g.tick")
.select("line")
.attr("stroke", "white");
yAxisG
.selectAll("g.tick")
.select("line")
.attr("stroke", "#bdbdbd");
svg
.selectAll("path.line")
.data([props.data])
.enter()
.append("path")
.attr("class", "line")
.attr("d", lineFunc)
.style("stroke", "steelblue")
.attr("fill", "none")
.attr("stroke-width", "6px")
.attr("stroke-dasharray", "0,1")
.each(function(d) {
this._mySave = `0,0`;
});
svg
.selectAll("path.line")
.transition()
.duration(750)
.attr("d", lineFunc)
.attrTween("stroke-dasharray", function () {
const length = this.getTotalLength();
const lastStartingValue = this._mySave.split(',')[1];
const int = d3.interpolate(`${lastStartingValue},${length}`, `${length},${length}`);
return (t) => {
this._mySave = int(t);
return this._mySave;
}
});
svg
.selectAll("circle.pricePoint")
.data(data2, d => d.time)
.enter()
.append("circle")
.transition()
.duration(750)
.attr("cx", d => onUpdateXScale(d.time))
.attr("cy", d => yScale(d.value))
.attr("class", "pricePoint")
.attr("r", 0)
.attr("fill", "white")
.style("opacity", 0)
svg
.selectAll("circle.pricePoint")
.transition()
.duration(750)
.attr("cx", d => onUpdateXScale(d.time))
.attr("r", d => 5)
.attr("stroke", "steelblue")
.attr("stroke-width", "3px")
.attr("fill", "white")
.style("opacity", 1);
svg
.selectAll("circle.pricePoint")
.data(data, d => d.time)
.exit()
.remove();
}
return Object.assign(svg.node(), {
update(props) {
onUpdateXScale
.domain(props.xDomain);
xAxisG
.transition()
.duration(750)
.call(xAxis, onUpdateXScale);
xAxisG.select(".domain").remove();
renderData(props);
}
});
}