D3.js与React - 看不到我画的线

时间:2017-10-09 05:54:17

标签: reactjs d3.js

以下是我正在使用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开发者工具,以显示正在绘制的线条我相信:

enter image description here 这是用于绘制线的代码(取自上面的代码):

//绘制线 - 添加到上下文 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));

任何建议都会非常感激 - 我想我很接近,但这不会让线条出现:)

三江源!

1 个答案:

答案 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));

因为你还没有发布一个工作小提琴,所以我会失明。希望这可以解决您的问题。