如何用React正确实现d3 svg点击?

时间:2017-02-15 09:20:48

标签: javascript reactjs d3.js svg data-visualization

我正在通过将其与React集成来创建d3地图。基本上,我有一个mapchart组件,可以按照这种方法创建地图:http://bl.ocks.org/herrstucki/9238916

我想通过点击特定的州/州/区等使我的地图可缩放,如此处所示https://bl.ocks.org/mbostock/2206590

所以,基本上我必须在svg上为click添加一个事件处理程序,如调用clicked()函数的第二个映射所示。

我尝试在React组件中实现clicked函数,如下所示:

 return React.DOM.svg({width: this.props.width, height: this.props.height},
  React.DOM.g({
    width: this.props.width,
    height: this.props.height,
  },
    React.DOM.path({
      className: 'states',
      d: path(states),
      onClick: this.clicked, // ** added click event here **
    }),
    React.DOM.path({
      className: 'state-borders',
      d: path(stateBorders),
    })
  ),
);

但是,在实现点击的功能时遇到了问题。

  clicked() {
    var states = select(ReactDOM.findDOMNode(this)).select('path.states');
    var d = states.attr('d');   // ** not what i thought it would be **

    var group = select(ReactDOM.findDOMNode(this)).select('g');

    var path = geoPath();
    var x, y, k;
    var centered;

    if (d && centered !== d) {
      var centroid = path.centroid(d);
      console.log(centroid)
      x = centroid[0];
      y = centroid[1];
      k = 4;
      centered = d;
    } else {
      x = this.props.width / 2;
      y = this.props.height / 2;
      k = 1;
      centered = null;
    }

    group.selectAll("path")
         .classed("active", centered && function(d) { return d === centered; });

    group.transition()
         .duration(750)
         .attr("transform", "translate(" + this.props.width / 2 + "," + this.props.height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
         .style("stroke-width", 1.5 / k + "px");
  }

path.centroid(d)无效,因为d的值似乎不是正确的格式,我不确定点击状态将如何突出显示。在正常的d3版本中,正确的状态和数据格式的d似乎被隐式传递给函数。如何让我的React组件功能做同样的事情?

1 个答案:

答案 0 :(得分:1)

首先,正如您所发现的,states.attr('d')正在返回路径的 d属性,即...中的<path d="..." />。地图示例中的d应该是被点击状态的数据,即绑定到被点击的DOM元素的数据,该元素又是来自你加载的TopoJSON文件。

由于您使用React而不是d3创建DOM节点,因此没有绑定到DOM元素的数据。这意味着您必须管理所单击的DOM节点与相关数据之间的关联。你可以这样做:

React.DOM.path({
  className: 'states',
  d: path(states),
  onClick: this.clicked.bind(this, states), // ** added click event here **
})

这样,states将作为第一个参数传递给clicked()

function clicked(d) {
  // now you have d!
}

然而,仅凭这种方法并不能让您知道点击了哪个实际状态,因为您将所有状态都绘制为单个<path>。 d3示例为每个状态创建一个路径,以便在单击路径时,其数据是该状态的数据对象。要在React中等效地执行此操作,您必须循环并创建多个路径。我提供正确的代码很困难,因为我不知道states的内容。但假设states是TopoJSON功能的数组,或多或少以下应该有效(在此处切换到JSX):

<svg width={this.props.width} height={this.props.height}>  
  <g>{
    states.map(function (state, i) {
      return (<path
        className='states'
        key={'state_' + i}
        d={path(state)}
        onClick={this.clicked.bind(this, state)}
      </path>)
    })
  }</g>
</svg>