d3-使用React的click事件由于质心坐标而响应缓慢

时间:2018-09-11 21:39:35

标签: javascript reactjs d3.js

我正在使用React和d3绘制美国地图。 我遇到了一个特殊的情况,我在path的jsx render元素上分配了一个clickhandler。

由于某种原因,我单击时会注意到,该事件似乎立即触发(如我可以控制台的日志所示),但是弹出状态要花几毫秒的时间。 如果我从应渲染的x元素中删除了ytext属性(请参见下文,以.state-label作为选择器),然后单击...它按预期方式响应。 不完全确定为什么这些text元素会导致事件从立即触发而延长,并希望您提出任何建议或想法。

import React, { Component } from "react";
import "components/general/NationalMap.css";
import * as d3 from "d3";
import { geoPath, geoAlbersUsa } from "d3-geo";
import {
  STATE_LABELS
} from "components/shared/Variables";

export default class NationalMap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  mapClicks = (event, d) => {
    console.log('clicked')
    this.setState({showPopover: true}) // 

  };

  projection = ($width, $height) => {
    return geoAlbersUsa()
      .scale($width * 1.1)
      .translate([$width / 2, $height / 2]);
  };

  path = () => {
    return d3.geoPath().projection(this.projection(1300, 1300 * 0.6));
  };

  getUSNamesX = d => {
    let path = this.path();
    let xCentroid = path.centroid(d)[0];

    if (d.properties.STUSPS === "FL") {
      xCentroid = xCentroid + 10;
    }
    return xCentroid || null;
  };

  getUSNamesY = d => {
    let path = this.path();
    let yCentroid = path.centroid(d)[1];

    if (d.properties.STUSPS === "FL") {
      yCentroid = yCentroid;
    }
    return yCentroid || null;
    //return !isNaN(path.centroid(d)[1]) ? yCentroid : null;
  };

  getStateLabel = abbrev => {
    let label;

    STATE_LABELS.forEach(l => {
      if (l.abbrev === abbrev) {
        label = l.label;
      }
    });
    return label;
  };

  getStateLabelClass = d => {
    let stateclass_name = this.getStateClass(d);

    if (typeof stateclass_name !== "undefined") {
      if (stateclass_name.includes("full")) {
        stateclass_name = "state-label-active";
      }
      return stateclass_name;
    }
  };

  getStateClass = d => {
    if (typeof this.props.stateData !== "undefined") {
      let sc = [];
      let stateAbbrev = d.properties.STUSPS;
      let state = this.props.stateData[d.properties.STUSPS];

      if (typeof state !== "undefined") {
        if (state.race === "Y") {
          if (typeof state.win != "undefined") {
            if (state.win != " ") {
              sc.push(stateAbbrev + " " + state.win + "full");
            } else {
              sc.push(stateAbbrev + " Noresult");
            }
          } else {
            sc.push(stateAbbrev);
          }
        } else if (state.race === "N") {
          sc.push(stateAbbrev + " Noelection");
        }
      }
      return sc.join().replace(",", " ");
    }
  };

  render() {
    const usMapData = this.props.usData;
    const usDistricts = this.props.usDistrictsData;
    const currentMap = this.props.currentMap;
    const $width = 1300;
    const $height = $width * 0.6;
    const LOADING = this.state.isReady;


    return (
      <div className="nat-map-wrap">
        <div className="map-wrapper">
        {showPopover &&
          <div>My Popover</div>
        }
          {!LOADING && (
            <div className="presgov-map ismap">
              <svg
                viewBox={"0 10 " + $width + " " + $height}
                className={"natMapWrap " + currentMap}
              >
                {usMapData && (
                  <React.Fragment>
                    <g className="state-g-tag">
                      <g id="states" className="zoom-g">
                        {usMapData.map((d, i) => (
                          <path
                            key={`path-${i}`}
                            d={geoPath().projection(
                              this.projection($width, $height)
                            )(d)}
                            className={"state " + this.getStateClass(d)}
                            stroke="#fff"
                            strokeWidth={0.5}
                            onClick={evt =>
                              this.mapClicks()
                            }
                          />
                        ))}
                      </g>
                    </g>
                    <g id="statenames">
                      <g className="zoom-g">
                        {usMapData.map((d, i) => (
                          <text
                            key={`text-${i}`}
                            x={this.getUSNamesX(d)}
                            y={this.getUSNamesY(d)}
                            textAnchor="middle"
                            className={
                              "state-label "
                            }
                          >
                            {d.properties.STUSPS}
                          </text>
                        ))}
                      </g>
                    </g>
                  </React.Fragment>
                )}
              </svg>
            </div>
          )}
        </div>
      </div>
    );
  }
}

1 个答案:

答案 0 :(得分:1)

点击时,您将执行4项昂贵的操作:

  1. this.path(),它同时启动了投影和路径!
  2. path.centroid(d)[0]
  3. this.path()
  4. path.centroid(d)[1]

首先,我将创建projectionpath作为成员变量,因此您无需在每次单击时都重新创建它们(两次)。其次,只调用一次path.centroid像这样的东西(所有代码未经测试):

getUSNamesXY = d => {

    let centroid = this.path.centroid(d); //path is member

    if (d.properties.STUSPS === "FL") {
      centroid[0] = centroid[0] + 10;
    }
    return centroid || null;
  };

并且:

<g className="zoom-g">
{usMapData.map((d, i) => (
  <text
    key={`text-${i}`}
    transform="translate(" + {this.getUSNamesXY(d)} + ")"
    textAnchor="middle"
    className={
      "state-label "
    }
  >
    {d.properties.STUSPS}
  </text>
))}
</g>