简体   繁体   English

将D3条形图转换为React D3条形图

[英]Convert D3 bar chart to React D3 bar chart

I am in the process of converting a vanilla D3 horizontal bar chart into a React D3 horizontal bar chart. 我正在将原始D3水平条形图转换为React D3水平条形图。 I can render the bars through React, but there are a few issues: 我可以通过React渲染栏,但是有一些问题:

  1. How do I spread the bar components (where width = matrix[x1] ) so that they do not stack upon one another? 如何散布条形分量(其中width = matrix[x1] ),以使它们不会彼此堆叠? --> resolved, see below -> 解决了,见下文
  2. How do I render the second set of bar components (where width = matrix[x0] ) on top of set 1? 如何在集合1的顶部渲染第二组条形分量(其中width = matrix[x0] )? --> resolved, see below -> 解决了,见下文
  3. How do I add transitions to the bar components? 如何向条形组件添加过渡?

I provide the working vanilla D3 first and the quasi-working React D3 second. 我首先提供工作正常的D3,然后提供准工作的React D3。

The data: 数据:

var matrix = [{y:0, x0:221, x1:2054},
 {y:1, x0:581, x1:1891},
 {y:2, x0:2485, x1:5128},
 {y:3, x0:135, x1:8849},
 {y:4, x0:31, x1:242}];

Vanilla D3 香草D3

// Constants
var width = 450,
    barHeight = 20,
    height = 300,
    delay = 500,
    duration = 750;

// Type Chart
var chart = d3.select('.typeBarChart')
  .attr('width', width)
  .attr('height', barHeight*matrix.length);

// X-axis
var maxCrime = d3.max(matrix, (d) => {
  return d.x1;
});

var x = d3.scale.linear()
    .domain([0, maxCrime+50])
    .range([0, width]);

var bar = chart.selectAll("g")
    .data(matrix)
    .enter()
    .append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });

bar.append("rect")
    .attr("fill","steelblue")
    .attr("width", 0)
    .attr("height", barHeight - 1)
  .transition()
  .delay(delay)
  .duration(duration)
  .ease("bounce")
    .attr("width", function(d) { return x(d.x1); })
    .attr("height", barHeight - 1);

bar.append("rect")
    .attr("fill","#E6550D")
    .attr("width", 0)
    .attr("height", barHeight - 1)
  .transition()
  .delay(delay)
  .duration(duration)
  .ease("bounce")
    .attr("width", function(d) { return x(d.x0); })
    .attr("height", barHeight - 1);

bar.append("text")
  .transition()
  .delay(delay+duration)
    .attr("x", function(d) {
      if (d.x1 < 1000) {
        return x(d.x1) + 20;
      } else {
        return x(d.x1) - 3;
      }
    })
    .attr("y", barHeight / 2)
    .attr("dy", ".35em")
    .text(function(d) { return d.x1; });

React Components 反应组件

const BarChart = React.createClass({
  render() {
    let { width, height, matrix } = this.props; // matrix data passed in from container component

    let maxCrime = d3.max(matrix, (d) => {return d.x1});

    let xScale = d3.scale.linear()
      .domain([0, maxCrime+50])
      .range([0, width]);

    return (
      <div>
        <h3>Rendering BarChart with React</h3>
        <svg
          width={width}
          height={height} >
            <DataSeries
              xScale={xScale}
              matrix={matrix} />
        </svg>
      </div>
    );
  }
});

const DataSeries = React.createClass({
  propTypes: {
    barHeight:          React.PropTypes.number,
    colors:             React.PropTypes.array,
    matrix:             React.PropTypes.array,
    xScale:             React.PropTypes.func
  },

  getDefaultProps() {
    return {
      barHeight:          20,
      matrix:             [],
      colors:             [
                            '#b2182b',
                            '#ef8a62',
                            '#fddbc7',
                            '#d1e5f0',
                            '#67a9cf',
                            '#2166ac'
                          ]
    };
  },

  render() {
    let { barHeight, matrix, colors, xScale } = this.props;
    let bars = matrix.map( (datum, index) => {
        return (
          <Bar
            key={index}
            width={xScale(datum.x1)}
            height={barHeight-1}
            fill={colors[index]}
          />
        );
      });

    return (
      <g>{bars}</g>
    );
  }    
});

const Bar = React.createClass({
  propTypes: {
    key:                React.PropTypes.number,
    width:              React.PropTypes.number,
    height:             React.PropTypes.number,
    colors:             React.PropTypes.array,
    matrix:             React.PropTypes.array,
    xScale:             React.PropTypes.func
  },

  getDefaultProps() {
    return {
      matrix:               [],
    };
  },

  render() {
    let { width, height, fill } = this.props;

    return (
      <rect
        width={width}
        height={height}
        fill={fill}
      />
    );
  }
});

I was able to triangulate 90% of what I needed with some Google Foo. 我可以用一些Google Foo三角剖分90%的需求。 The only part still missing is the transitions. 唯一仍然缺少的部分是过渡。 I will submit an edit if I can resolve that before someone else. 如果可以先解决,我将提交修改。

export const BarChart = React.createClass({

  propTypes: {
    width:  React.PropTypes.number,
    height: React.PropTypes.number,
    data:   React.PropTypes.array.isRequired
  },

  getDefaultProps() {
    return {
      width: 450,
      height: 300
    };
  },

  structureData(districtNumber) {
    let { data, districtInfo } = this.props;

    if (districtNumber) {
      data = data.filter( (glob) => {
        return glob.district == districtNumber;
      });
    }

    const nestedData = d3.nest()
      .key(function(d) {
        return d.type;
      })
      .rollup(function (v) {
        return d3.sum(v, function(d) {return parseInt(d.count);} );
      })
      .entries(data)
      .sort((a, b) => {
        var nameA = a.key.toLowerCase(), nameB = b.key.toLowerCase();
        if (nameA < nameB) //sort string ascending
          return -1;
        if (nameA > nameB)
          return 1;
        return 0;
      });

    return nestedData;
  },

  matrixData(total, district) {
    let matrix = [];

    for (var i=0; i<total.length; i++) {
      var obj = {};
      obj['y'] = i;
      obj['x0'] = district[i].values;
      obj['x1'] = total[i].values;
      matrix.push(obj);
    }

    return matrix;

  },

  render() {
    let { width, height, districtInfo } = this.props;
    const dataTotal = this.structureData();
    const dataDistrict = this.structureData(districtInfo.district_number);

    const matrix = this.matrixData(dataTotal, dataDistrict);

    let maxCrime = d3.max(dataTotal, (d) => {return d.values});

    let xScale = d3.scale.linear()
      .domain([0, maxCrime+50])
      .range([0, width]);

    return (
      <div>
        <svg
          width={width}
          height={height} >
            <DataSeries
              xScale={xScale}
              matrix={matrix} />
        </svg>
      </div>
    );
  }
});

const DataSeries = React.createClass({
  propTypes: {
    barHeight:          React.PropTypes.number,
    colors:             React.PropTypes.array,
    matrix:             React.PropTypes.array,
    xScale:             React.PropTypes.func
  },

  getDefaultProps() {
    return {
      barHeight:          20,
      matrix:             [],
      // interpolationType:  'cardinal',
      colors:             [
                            '#b2182b',
                            '#ef8a62',
                            '#fddbc7',
                            '#d1e5f0',
                            '#67a9cf',
                            '#2166ac'
                          ]
    };
  },

  render() {
    let { barHeight, matrix, colors, xScale } = this.props;
    let barsTotal = matrix.map( (datum, index) => {

        let x = datum.x1 < 1000 ? xScale(datum.x1) + 20 : xScale(datum.x1) - 3;

        const barSettings = {
          datum: datum,
          width: xScale(datum.x1),
          height: barHeight-1,
          fill: colors[index],

        };

        const textSettings = {
          x: x,
          y: barHeight / 2,
          dy: ".35em",
        };

        return (
          <g key={index} transform={`translate(0, ${index*barHeight})`} >
            <Bar {...barSettings} />
            <text {...textSettings}>{datum.x1}</text>
          </g>
        );
      });

    let barsDistrict = matrix.map( (datum, index) => {

        const barSettings = {
          key: index,
          width: xScale(datum.x0),
          height: barHeight-1,
          fill: 'gray',
          transform: `translate(0, ${index*barHeight})`
        };

        return (
          <Bar {...barSettings} />
        );
      });

    return (
      <g>
        <g>{barsTotal}</g>
        <g>{barsDistrict}</g>
      </g>
    );
  }

});

const Bar = React.createClass({
  propTypes: {
    width:              React.PropTypes.number,
    height:             React.PropTypes.number,
    colors:             React.PropTypes.array,
    matrix:             React.PropTypes.array,
    xScale:             React.PropTypes.func
  },

  getDefaultProps() {
    return {
      matrix:               [],
    };
  },

  render() {
    return (
      <rect {...this.props} />
    );
  }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM