首页 > 解决方案 > 如何在这个 d3 气泡图中输入自己的数据以做出反应?

问题描述

所以在 React 中,我复制并粘贴了一个 d3 气泡图元素,但对 React 了解不多。我使用自己的 json 数据将数据输入到气泡图中。但是,当我运行它时,由于 d.data.Name.substring(0, dr / 3),它给出了“无法从未定义中读取”。我觉得我没有正确定义我的 json 对象(d3.hierachy 和一些函数似乎要求孩子或其他东西),这是我的代码:

路线图列表.json:

    [
  {
    "Name": "Computer Science",
    "Count": 4156
  },

  {
    "Name": "Home Economics",
    "Count": 689
  },
  {
    "Name": "Venture Capital Investment",
    "Count": 890
  },
  {
    "Name": "Fabric design",
    "Count": 167
  },
  {
    "Name": "Deep Learning Researchers",
    "Count": 812
  }
]

顺便说一下,在我的气泡图组件 Bubble.js 中,当我使用状态时,它可以工作,但是由于我想要动态更新,所以我正在使用 this.props.roadmapdata

泡泡.js:

import React, { Component } from "react";
import * as d3 from "d3";

class Bubble extends Component {
  constructor(props) {
    super(props);
  }

  componentWillReceiveProps(nextProps) {
    d3.select("nodes").remove("nodes");
    var diameter = 600;

    var color = d3.scaleOrdinal(d3.schemeCategory10);

    var bubble = d3
      .pack(this.props.RoadmapData)
      .size([diameter, diameter])
      .padding(1.5);

    var svg = d3
      .select("body")
      .append("svg")
      .attr("width", diameter)
      .attr("height", diameter)
      .attr("class", "bubble");

    var node = svg
      .selectAll(".node")
      .data(this.props.RoadmapData)
      .enter()
      .filter(function(d) {
        return !d.children;
      })
      .append("g")
      .attr("class", "node")
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });
    node.append("title").text(function(d) {
      return d.Name + ": " + d.Count;
    });

    node
      .append("circle")
      .attr("r", function(d) {
        return d.r;
      })
      .style("fill", function(d, i) {
        return color(i);
      });

    node
      .append("text")
      .attr("dy", ".2em")
      .style("text-anchor", "middle")
      .text(function(d) {
        return d.data.Name.substring(0, d.r / 3);
      })
      .attr("font-family", "sans-serif")
      .attr("font-size", function(d) {
        return d.r / 5;
      })
      .attr("fill", "white");

    node
      .append("text")
      .attr("dy", "1.3em")
      .style("text-anchor", "middle")
      .text(function(d) {
        return d.data.Count;
      })
      .attr("font-family", "Gill Sans", "Gill Sans MT")
      .attr("font-size", function(d) {
        return d.r / 5;
      })
      .attr("fill", "white");
  }

  render() {
    return <div>{this.node}</div>;
  }
}

export default Bubble;

谢谢!

标签: reactjsd3.js

解决方案


这是一个使用气泡图并使用d3.packSiblings做出反应的示例。

<svg>元素有一个引用,所以当组件被挂载时,你可以使用 d3 来创建/更新/删除元素。

笔记:

dataForPacking只是一个映射,用于创建 d3 打包所需的属性,例如 r,x,z,然后是您的数据名称、计数。

有一个输入框,用于说明 setState 用新数据更新初始道具数据并重新渲染气泡。有很多更好的方法可以做到这一点,但这是一个好的开始。

const dataJSON = [
  {
    Name: "Computer Science",
    Count: 4156
  },

  {
    Name: "Home Economics",
    Count: 689
  },
  {
    Name: "Venture Capital Investment",
    Count: 890
  },
  {
    Name: "Fabric design",
    Count: 167
  },
  {
    Name: "Deep Learning Researchers",
    Count: 812
  }
];

const dataForPacking = (data) => {
    return {
      r: data.Count,
      x: 0,
      y: 0,
      Count: data.Count,
      Name: data.Name
    };
};

class Bubble extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      width: this.props.size[0],
      height: this.props.size[1],
      data: this.props.data
    };
    this.svgRef = React.createRef();
    this.drawBubble = this.drawBubble.bind(this);
  }
  componentDidMount() {
    this.drawBubble()
  }
  componentDidUpdate() {
    this.drawBubble()
  }
  drawBubble = () => {
    const svg = d3.select(this.svgRef.current);
    svg.select("g").remove();
    const diameter = 600;

    const color = d3.scaleOrdinal(d3.schemeCategory10);

    const circles = svg
      .append("g")
      .attr("class", "circles")
      .attr(
        "transform",
        `translate(${this.state.width / 2},
          ${this.state.height / 2})scale(0.02)`
      );

    const node = circles
      .selectAll(".node")
      .data(d3.packSiblings(this.state.data))
      .enter()
      .append("g")
      .attr("class", "node")
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });

    node.append("title").text(function(d) {
      return d.Name + ": " + d.Count;
    });

    node
      .append("circle")
      .attr("r", function(d) {
        return d.r;
      })
      .style("fill", function(d, i) {
        return color(i);
      });

    node
      .append("text")
      .attr("dy", ".2em")
      .style("text-anchor", "middle")
      .text(function(d) {
        return d.Name.substring(0, d.r / 3);
      })
      .attr("font-family", "sans-serif")
      .attr("font-size", function(d) {
        return d.r / 5;
      })
      .attr("fill", "white");

    node
      .append("text")
      .attr("dy", "1.3em")
      .style("text-anchor", "middle")
      .text(function(d) {
        return d.Count;
      })
      .attr("font-family", "Gill Sans", "Gill Sans MT")
      .attr("font-size", function(d) {
        return d.r / 5;
      })
      .attr("fill", "white");
  };
  keyPress = event => {
    if (event.key === "Enter") {
      const newDataArray = event.currentTarget.value.split(",");
      if(!newDataArray[1] || isNaN(newDataArray[1])){
        return; 
      }
      const newData = {
        Name: newDataArray[0],
        Count: Number(newDataArray[1])
      };
      this.setState(prevState => ({
        data: [...prevState.data, dataForPacking(newData)]
      }))
      event.currentTarget.value = "";
    }
  };
  reset = () => {
      this.setState(prevState => ({
        data: this.props.data.map(e => dataForPacking(e))
      }))
  };
  render() {
    return (
      <div>
        <span>Name, Count </span>
        <input type="text" onKeyPress={this.keyPress} />
        <button onClick={this.reset}>reset</button>
        <svg
          ref={this.svgRef}
          width={this.props.size[0]}
          height={this.props.size[1]}
        />
      </div>
    );
  }
}
ReactDOM.render(
  <Bubble data={dataJSON.map(e=> dataForPacking(e))} size={[600, 200]} />,
  document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0/umd/react-dom.production.min.js"></script>
<div id="app"><div>


推荐阅读