以下是我正在使用React& amp; D3:
import React from 'react'
import * as d3 from 'd3'
import { Card } from 'material-ui'
import CardBanner from './CardBanner.js'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import HfcLiveModemPerfChartOptions from './HfcLiveModemPerfChartOptions.js'
// D3 Line (Interpolation Types):
// curveLinear
// curveStep
// curveStepBefore
// curveStepAfter
// curveBasis
// curveCardinal
// curveMonotoneX
// curveCatmullRom
class HfcLiveModemPerfChart extends React.Component {
constructor(props) {
super(props);
let chartHeight = 0;
let chartWidth = 0;
// Axis Length
const heightInit = 249; // Affects Y-Axis Height (Runs Top Down)
const widthInit = 950; // Affects X-Axis Width (Runs Left to Right)
// D3 Date Parser - Convert a Date String into a Date Object
const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
this.state = {
canvasHeight: 0,
canvasWidth: 0,
canvasSizeUpdated: false,
dataArray: [{x:25, y:50},{x:30, y:15},{x:50, y:70},{x:110, y:18},{x:140, y:80}],
chartData: [{"name":"4.0E-6MHz","value":"26.2","date":"2017-08-25 21:00:00"},{"name":"4.0E-6MHz","value":"23.8","date":"2017-08-25 22:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-25 23:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 00:00:00"},{"name":"4.0E-6MHz","value":"33.7","date":"2017-08-26 01:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 02:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 03:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 04:00:00"},{"name":"4.0E-6MHz","value":"31.9","date":"2017-08-26 05:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 06:00:00"},{"name":"4.0E-6MHz","value":"6.2","date":"2017-08-26 07:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 08:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 09:00:00"},{"name":"4.0E-6MHz","value":"21.7","date":"2017-08-26 10:00:00"},{"name":"4.0E-6MHz","value":"22.3","date":"2017-08-26 11:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 12:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 13:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 14:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 15:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 16:00:00"},{"name":"4.0E-6MHz","value":"96.2","date":"2017-08-26 17:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 18:00:00"},{"name":"4.0E-6MHz","value":"33.5","date":"2017-08-26 19:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 20:00:00"},{"name":"4.0E-6MHz","value":"44.5","date":"2017-08-26 21:00:00"}],
// // D3 Line
// line: d3.line()
// .x(function(d,i){ return d.x*6; })
// .y(function(d,i){ return d.y*4; })
// .curve(d3.curveLinear),
// // D3 Line
// line: d3.line()
// .x(function(d,i){ return xAxisScale(d.date); })
// .y(function(d,i){ return yAxisScale(d.value); })
// .curve(d3.curveLinear),
height: heightInit,
width: widthInit,
yAxisScale: '',
xAxisScale: '',
// // Y-Axis Scale (Min/Max)
// yAxisScale: d3.scaleLinear()
// .domain(d3.extent(this.chartData, function(d) {return d.value;}))
// .range([heightInit, 0]),
// // X-Axis Date Range
// xAxisScale: d3.scaleTime()
// .domain(d3.extent(this.chartData, function(d){ return parseDate(d.date) }))
// .range([0,widthInit]),
margin: {left:55,right:10,top:30,bottom:0} // Positioning Y-Axis
};
// X-Axis Date Range
this.state.xAxisScale = d3.scaleTime()
.domain(d3.extent(this.state.chartData, function(d){ return parseDate(d.date) }))
.range([0,widthInit]);
// Y-Axis Scale (Min/Max)
this.state.yAxisScale = d3.scaleLinear()
.domain(d3.extent(this.state.chartData, function(d) {return d.value;}))
.range([heightInit, 0]);
}
componentDidUpdate() {
if(this.props.contentRender && !this.state.canvasSizeUpdated) {
this.setState({
canvasSizeUpdated: true,
canvasHeight: document.getElementById('HfcLiveModemPerfChart').clientHeight,
canvasWidth: document.getElementById('HfcLiveModemPerfChart').clientWidth
});
}
}
// Dynamically Update Width/Height on Browser Resize
updateDimensions() {
if(document.getElementById('HfcLiveModemPerfChart') != null) {
this.chartHeight = document.getElementById('HfcLiveModemPerfChart').clientHeight;
this.chartWidth = document.getElementById('HfcLiveModemPerfChart').clientWidth;
}
}
// Create Context - RAW SVG Required for all Operations
setContext() {
this.updateDimensions();
let width = this.chartWidth - 50;
return d3.select(this.refs.line).append('svg') // Select the Element to work with via REF & Append SVG
.attr('height', this.chartHeight) // Adding Attributes to SVG - Chaining Attributes
.attr('width', this.chartWidth)
.attr('id', 'D3lineChart') // Gives the SVG an ID Access via DOM/CSS
.append('g')
.attr('transform', 'translate(15, -15)')
}
// Add D3 Events - ToolTip & Interactive Line
addD3Events(context, xAxisScale) {
// Stop ToolTip Moving
let toolTipMoving = true;
return context
.on("mousemove", function(d) {
if(toolTipMoving) {
// Hover Line Let Offset
let position = document.getElementById('HfcLiveModemPerfChart').getBoundingClientRect();
let body = document.body;
let docEl = document.documentElement;
let scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
let clientLeft = docEl.clientLeft || body.clientLeft || 0;
let toolTipXOffset = position.left + scrollLeft - clientLeft + 15;
// Hover Line - Position & Show
d3.select('.interactiveHoverLine')
.attr("x1", d3.event.pageX - toolTipXOffset)
.attr("x2", d3.event.pageX - toolTipXOffset)
.classed("hidden", false);
// ToolTip
d3.select('#tooltip')
.classed("hidden", false)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY + "px")
.select("#value")
.text(d);
}
})
.on("mouseout", function() {
if(toolTipMoving) {
d3.select("#tooltip").classed("hidden", true);
d3.select(".interactiveHoverLine").classed("hidden", true);
}
})
// Lock ToolTip Location
.on("click", function() {
toolTipMoving = toolTipMoving ? false : true;
});
}
// Draw Interactive Hover Line
// https://stackoverflow.com/questions/42433779/d3-js-line-chart-tooltip-and-vertical-line-of-hover
drawHoverLine(context) {
return context
.append("line")
.attr('class', 'interactiveHoverLine')
.attr("x1", 0).attr("x2", 0)
.attr("y1", 30).attr("y2", this.state.canvasHeight - 60)
}
// Draw Legend
drawLegend(context) {
return context;
// http://bl.ocks.org/dan-delaney/65f9032e22c1c53b52e2ef7e4044dd6d
}
// Draw Line - Add to Context
drawLine(context) {
// D3 Date Parser - Convert a Date String into a Date Object
const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
// Axis Length
const heightInit = 249; // Affects Y-Axis Height (Runs Top Down)
const widthInit = 950; // Affects X-Axis Width (Runs Left to Right)
// X-Axis Date Range
const xAxisScale = d3.scaleTime()
.domain(d3.extent(this.state.chartData, function(d){ return parseDate(d.date) }))
.range([0,widthInit]);
// Y-Axis Scale (Min/Max)
const yAxisScale = d3.scaleLinear()
.domain(d3.extent(this.state.chartData, function(d) {return d.value;}))
.range([heightInit, 0]);
// D3 Line
let line = d3.line()
.x(function(d){ return xAxisScale(d.date) })
.y(function(d){ return yAxisScale(d.value) })
.curve(d3.curveLinear);
console.dir(this.state.chartData);
console.dir(this.state.xAxisScale);
console.dir(this.state.yAxisScale);
return context
.append('path')
.attr('class', 'chartLinesGroup')
.append('g')
.attr('stroke', 'black')
.attr('fill', 'black')
.attr('d', line(this.state.chartData));
// return context;
// // Y-Axis Scale (Min/Max)
// yAxisScale: d3.scaleLinear()
// .domain(d3.extent(data, function(d) {return d.value;}))
// .range([heightInit, 0]),
//
// // X-Axis Date Range
// xAxisScale: d3.scaleTime()
// .domain(d3.extent(data, function(d){ return parseDate(d.date) }))
// .range([0,widthInit]),
// const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
// let line = d3.line()
// // .interpolate("basis")
// .x(function(d) { return this.parseDate(d.date) })
// .y(function(d) { return d.value });
// group.selectAll(".line")
// .data(series)
// .enter().append("path")
// .attr("class", "line")
// .attr("d", line);
// return context
// .attr('d', this.state.line(this.state.dataArray))
// // .enter()
// .append('path')
// .attr('stroke', 'black')
// .attr('fill', 'none')
// .attr("class", "line")
// .attr('d', line)
// .append('g');
}
// Draw Circles - Data Point Dots
drawCirles(context) {
return context
.append('g') // This allows Grouping the Circles into one Unit - They Transform as desired
.attr('class', 'circleGroup')
.selectAll("circle")
.data(this.state.dataArray)
.enter()
.append('circle')
.attr('class', function(d,i){ return 'group'+i; }) // Styling Group - Group0, Group1, Group2 etc... (Class Name)
.attr('cx', function(d,i){ return d.x*6; })
.attr('cy', function(d,i){ return d.y*4; })
.attr('r', '2');
// let bisectDate = d3.bisector(function(d) { return d.class; }).left;
//
// console.dir(bisectDate);
//
// let mouse_x = d3.mouse(this)[0];
// let mouse_y = d3.mouse(this)[1];
//
//
// let mouseDate = xScale.invert(mouse_x);
// let i = bisectDate(this.state.data, mouseDate);
//
// let d0 = this.state.data[i - 1]
// let d1 = this.state.data[i];
// let d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;
}
// Draw Y-Axis
drawYAxis(context) {
return context
.append('g') // Group Y-Axis Element
.attr('class', 'yAxisGroup') // Add Class
.attr('transform', 'translate('+this.state.margin.left+','+this.state.margin.top+') scale(1.5)') // move Y-Axis Scales (Uses React State)
.call(d3.axisLeft(this.state.yAxisScale).tickPadding(5).tickSize(7.5)) // Add Y-Axis & Limits number of Ticks Shown
}
// Draw X-Axis
drawXAxis(context) {
let height = this.chartHeight - 60;
const formatDate = d3.timeFormat("%b %d %H:%M");
return context
.append('g') // Group X-Axis Element
.attr('class', 'xAxisGroup') // Add Class
.attr('transform', 'translate(55,'+height+') scale(1.5)') // Move X-Axis to Bottom of Chart & Size/Width of Axis
.call(d3.axisBottom(this.state.xAxisScale).tickPadding(5).tickSize(7.5).tickFormat(formatDate)) // Add X-Axis & Limits number of Ticks Shown
}
// Draw Y-Axis Label
drawYAxisLabel(context) {
return context
.append('g')
.append("text")
.attr('class', 'yAxisLabelGroup')
.attr("y", 5)
.attr("x",0 - (this.chartHeight / 2))
.attr("transform", "rotate(-90)")
.style("text-anchor", "middle")
.text("Values MHz");
}
// Draw X-Axis Label
drawXAxisLabel(context) {
return context
.append('g')
.append("text")
.attr('class', 'xAxisLabelGroup')
.attr("transform", "translate(" + (this.chartWidth/2) + " ," + (this.chartHeight - 15) + ")")
.style("text-anchor", "middle")
.text("DateTime");
}
// Append Color Bands
appendColorBands(context) {
this.redColorBandTop(context)
this.orangeColorBandTop(context)
this.greenColorBandMiddle(context)
this.orangeColorBandBottom(context)
this.redColorBandBottom(context)
}
// Red Color Band Top
redColorBandTop(context) {
return context
.append('rect')
.attr('class', 'colorBandsGroup')
.attr("fill", "#800")
.attr("stroke", "#800")
.attr('opacity', '0.35')
.attr("x", 55)
.attr("y", 30)
.attr("width", this.state.canvasWidth - 115)
.attr("height", 75);
}
// Orange Color Band Top
orangeColorBandTop(context) {
return context
.append('rect')
.attr('class', 'colorBandsGroup')
.attr("fill", "#f90")
.attr("stroke", "#f90")
.attr('opacity', '0.35')
.attr("x", 55)
.attr("y", 105)
.attr("width", this.state.canvasWidth - 115)
.attr("height", 75);
}
// Green Color Band Middle
greenColorBandMiddle(context) {
return context
.append('rect')
.attr('class', 'colorBandsGroup')
.attr("fill", "#005e23")
.attr("stroke", "#005e23")
.attr('opacity', '0.35')
.attr("x", 55)
.attr("y", 180)
.attr("width", this.state.canvasWidth - 115)
.attr("height", 75);
}
// Orange Color Band Bottom
orangeColorBandBottom(context) {
return context
.append('rect')
.attr('class', 'colorBandsGroup')
.attr("fill", "#f90")
.attr("stroke", "#f90")
.attr('opacity', '0.35')
.attr("x", 55)
.attr("y", 255)
.attr("width", this.state.canvasWidth - 115)
.attr("height", 75);
}
// Red Color Band Bottom
redColorBandBottom(context) {
return context
.append('rect')
.attr('class', 'colorBandsGroup')
.attr("fill", "#800")
.attr("stroke", "#800")
.attr('opacity', '0.35')
.attr("x", 55)
.attr("y", 330)
.attr("width", this.state.canvasWidth - 115)
.attr("height", 75);
}
render() {
if(this.props.contentRender) { // Conditional Rendering
// Re-Draw Chart
const context = this.setContext()
this.drawHoverLine(context)
this.drawLine(context)
this.drawCirles(context)
this.drawYAxis(context)
this.drawXAxis(context)
this.appendColorBands(context)
this.drawYAxisLabel(context)
this.drawXAxisLabel(context)
this.addD3Events(context, this.state.xAxisScale)
this.drawLegend(context)
return (
<div>
<Card id="HfcLiveModemPerfChart" className="cardContainer">
<CardBanner
cardTitle="HFC Live Modem Performance Chart"
cardType="HfcLiveModemPerfChart"
/>
{/*<HfcLiveModemPerfChartOptions />*/}
<div id="HfcLiveModemPerfChartOptionsContainer">
<HfcLiveModemPerfChartOptions />
</div>
<div id="tooltip" className="hidden">
<table>
<thead>
<tr>
<td colSpan="3">
<strong className="x-value">24/8/2017 10:00 AEST</strong>
</td>
</tr>
</thead>
<tbody>
<tr>
<td className="legend-color-guide"><div></div></td>
<td className="key">1.0E-6MHz</td>
<td className="value">46.50</td>
</tr><tr>
<td className="legend-color-guide"><div></div></td>
<td className="key">2.0E-6MHz</td>
<td className="value">47.00</td>
</tr><tr>
<td className="legend-color-guide"><div></div></td>
<td className="key">4.0E-6MHz</td>
<td className="value">45.80</td>
</tr><tr>
<td className="legend-color-guide"><div></div></td>
<td className="key">5.0E-6MHz</td>
<td className="value">45.50</td>
</tr>
</tbody>
</table>
</div>
<div
ref="line"
className="d3LineChartContainer"
>
</div>
</Card>
</div>
)
}else{return false}
}
}
HfcLiveModemPerfChart.propTypes = {
contentRender: PropTypes.bool
};
const mapStateToProps = (state) => {
return {
contentRender: state.setHfcLiveModemPerfChartComponentStatus.setHfcLiveModemPerfChartComponentStatusState
}
}
export default connect(mapStateToProps)(HfcLiveModemPerfChart);
以下输出内容包括Chrome开发者工具,以显示正在绘制的线条我相信:
//绘制线 - 添加到上下文 drawLine(context){
// D3 Date Parser - Convert a Date String into a Date Object
const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
// Axis Length
const heightInit = 249; // Affects Y-Axis Height (Runs Top Down)
const widthInit = 950; // Affects X-Axis Width (Runs Left to Right)
// X-Axis Date Range
const xAxisScale = d3.scaleTime()
.domain(d3.extent(this.state.chartData, function(d){ return parseDate(d.date) }))
.range([0,widthInit]);
// Y-Axis Scale (Min/Max)
const yAxisScale = d3.scaleLinear()
.domain(d3.extent(this.state.chartData, function(d) {return d.value;}))
.range([heightInit, 0]);
// D3 Line
let line = d3.line()
.x(function(d){ return xAxisScale(d.date) })
.y(function(d){ return yAxisScale(d.value) })
.curve(d3.curveLinear);
console.dir(this.state.chartData);
console.dir(this.state.xAxisScale);
console.dir(this.state.yAxisScale);
return context
.append('path')
.attr('class', 'chartLinesGroup')
.append('g')
.attr('stroke', 'black')
.attr('fill', 'black')
.attr('d', line(this.state.chartData));
任何建议都会非常感激 - 我想我很接近,但这不会让线条出现:)
三江源!
答案 0 :(得分:1)
第一个问题:
您没有解析日期parseDate
。
let line = d3.line()
.x(function(d){ return xAxisScale(d.date) })
.y(function(d){ return yAxisScale(d.value) })
.curve(d3.curveLinear);
应该是
let line = d3.line()
.x(function(d){ return xAxisScale(parseDate(d.date)) })
.y(function(d){ return yAxisScale(d.value) })
.curve(d3.curveLinear);
第二个问题:
您要将d属性添加到g(组),它应该添加到路径中。
return context
.append('g')
.append('path')
.attr('class', 'chartLinesGroup')
.attr('stroke', 'black')
.attr('fill', 'black')
.attr('d', line(this.state.chartData));
因为你还没有发布一个工作小提琴,所以我会失明。希望这可以解决您的问题。