渲染非硬编码的D3代码

Ale*_*ach 5 d3.js reactjs

我注意到在大多数D3文档中,图表,图形,边距等通常都是硬编码的.xAxis是500px等.这对我没有多大帮助.所以我试着想一想如何在React中实现渲染D3内容的动态方法.

例如,在下面的代码中,我只是根据一些时间序列股票价格数据渲染一条线.我有一些D3代码,componentDidMount但考虑到D3的工作方式,它需要具体的宽度和高度值.但是componentDidMount我还没有这些价值观.让我们说这个单线图是网格布局中div中的100个其中一个图.

那么我如何获得div/svg的宽度/高度,然后才计算我的D3代码并渲染svgs?

componentDidMount() {

        console.log("componentDidMount")


        const data = this.props.data;
        const selectX = this.props.selectX;
        const selectY = this.props.selectY;

        console.log(data)

        const xScale = d3ScaleTime()
            .domain(d3ArrayExtent(data, selectX))
            .range([0, 1]);

        const yScale = d3ScaleTime()
            .domain(d3ArrayExtent(data, selectY))
            .range([1, 0]);

        const xAxis = d3AxisBottom()
            .scale(xScale)
            .ticks(data.length / 8);

        const yAxis = d3AxisLeft()
            .scale(yScale)
            .ticks(3);

        const selectScaledX = datum => xScale(selectX(datum));
        const selectScaledY = datum => yScale(selectY(datum));

        const sparkLine = d3Line()
            .x(selectScaledX)
            .y(selectScaledY);

        const linePath = sparkLine(data);

        console.log(linePath);

        this.setState({
            linePath: linePath
        });
    }
Run Code Online (Sandbox Code Playgroud)

Mik*_*kov 2

我尝试在下面的演示中模仿您的问题。

class Chart extends React.Component {
  componentDidMount() {
  	var data = this.props.data;
		var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
    var chartHeight = containerDOMElementWidth / 2;

		this.drawChart(data, containerDOMElementWidth, chartHeight);
  }
  
  drawChart(data, chartWidth, chartHeight) {
    var margin = { top: 30, right: 20, bottom: 30, left: 50 };

    var width = chartWidth - margin.left - margin.right;
    var height = chartHeight - margin.top - margin.bottom;

    var parseDate = d3.timeParse("%d-%b-%y");

    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    var xAxis = d3.axisBottom().scale(x)
        .ticks(2);

    var yAxis = d3.axisLeft().scale(y)
        .ticks(2);

    var valueline = d3.line()
        .x(function (d) {
          return x(d.date);
        })
        .y(function (d) {
          return y(d.close);
        });

    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    data.forEach(function (d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

    // Scale the range of the data
    x.domain(d3.extent(data, function (d) {
        return d.date;
        }));
    y.domain([0, d3.max(data, function (d) {
        return d.close;
        })]);

    svg.append("path").attr('class', 'line-chart') // Add the valueline path.
    .attr("d", valueline(data));

    svg.append("g") // Add the X Axis
    .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g") // Add the Y Axis
    .attr("class", "y axis")
        .call(yAxis);
  }
  
  render() {
    return <div></div>;
  }
}

function getRandomData() {
	return [{
        date: "1-May-12",
        close: Math.random() * 90
    }, {
        date: "30-Apr-12",
        close: Math.random() * 90
    }, {
        date: "27-Apr-12",
        close: Math.random() * 90
    }, {
        date: "26-Apr-12",
        close: Math.random() * 90
    }, {
        date: "25-Apr-12",
        close: Math.random() * 90
    }];
}

ReactDOM.render(
  <div className="charts-container">
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
        <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
  </div>,
  document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)
.line-chart {
  fill: none;
  stroke: blue
}

.charts-container {
  display: flex;
}

.chart-wrapper {
  width: 100%;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="container">
</div>
Run Code Online (Sandbox Code Playgroud)

这里我们一一绘制两个图表,并这样计算它们的宽度和高度:

componentDidMount() {
  var data = this.props.data;

  // gets the width of container div element with ReactDOM.findDOMNode
  var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width

  // chart height have to be a half of width 
  var chartHeight = containerDOMElementWidth / 2;

  // pass width and height as an arguments
  this.drawChart(data, containerDOMElementWidth, chartHeight);
}
Run Code Online (Sandbox Code Playgroud)

我们的drawChart方法如下所示:

drawChart(data, chartWidth, chartHeight) {
  var margin = { top: 30, right: 20, bottom: 30, left: 50 };

  var width = chartWidth - margin.left - margin.right;
  var height = chartHeight - margin.top - margin.bottom;
  ... // code for the chart drawing 
Run Code Online (Sandbox Code Playgroud)

render:

ReactDOM.render(
  <div className="charts-container">
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
  </div>,
  document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)

如果我们只渲染一张图表,它也可以正常工作,无需更改任何代码,因为我们将图表的宽度计算为父div元素的宽度:

componentDidMount() {
  var data = this.props.data;

  // gets the width of container div element with ReactDOM.findDOMNode
  var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width

  // chart height have to be a half of width 
  var chartHeight = containerDOMElementWidth / 2;

  // pass width and height as an arguments
  this.drawChart(data, containerDOMElementWidth, chartHeight);
}
Run Code Online (Sandbox Code Playgroud)
drawChart(data, chartWidth, chartHeight) {
  var margin = { top: 30, right: 20, bottom: 30, left: 50 };

  var width = chartWidth - margin.left - margin.right;
  var height = chartHeight - margin.top - margin.bottom;
  ... // code for the chart drawing 
Run Code Online (Sandbox Code Playgroud)
ReactDOM.render(
  <div className="charts-container">
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
  </div>,
  document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)

三个图表的情况相同:

class Chart extends React.Component {
  componentDidMount() {
  	var data = this.props.data;
		var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
    var chartHeight = containerDOMElementWidth / 2;

		this.drawChart(data, containerDOMElementWidth, chartHeight);
  }
  
  drawChart(data, chartWidth, chartHeight) {
    var margin = { top: 30, right: 20, bottom: 30, left: 50 };

    var width = chartWidth - margin.left - margin.right;
    var height = chartHeight - margin.top - margin.bottom;

    var parseDate = d3.timeParse("%d-%b-%y");

    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    var xAxis = d3.axisBottom().scale(x)
        .ticks(2);

    var yAxis = d3.axisLeft().scale(y)
        .ticks(2);

    var valueline = d3.line()
        .x(function (d) {
          return x(d.date);
        })
        .y(function (d) {
          return y(d.close);
        });

    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    data.forEach(function (d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

    // Scale the range of the data
    x.domain(d3.extent(data, function (d) {
        return d.date;
        }));
    y.domain([0, d3.max(data, function (d) {
        return d.close;
        })]);

    svg.append("path").attr('class', 'line-chart') // Add the valueline path.
    .attr("d", valueline(data));

    svg.append("g") // Add the X Axis
    .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g") // Add the Y Axis
    .attr("class", "y axis")
        .call(yAxis);
  }
  
  render() {
    return <div></div>;
  }
}

function getRandomData() {
	return [{
        date: "1-May-12",
        close: Math.random() * 90
    }, {
        date: "30-Apr-12",
        close: Math.random() * 90
    }, {
        date: "27-Apr-12",
        close: Math.random() * 90
    }, {
        date: "26-Apr-12",
        close: Math.random() * 90
    }, {
        date: "25-Apr-12",
        close: Math.random() * 90
    }];
}

ReactDOM.render(
  <div className="charts-container">
    <div className="chart-wrapper">
      <Chart data={getRandomData()} />
    </div>
  </div>,
  document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)
.line-chart {
  fill: none;
  stroke: blue
}

.charts-container {
  display: flex;
}

.chart-wrapper {
  width: 100%;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="container">
</div>
Run Code Online (Sandbox Code Playgroud)