d3.js图形Bug拖动和放大/缩小-react.js

时间:2020-05-21 20:43:32

标签: javascript html reactjs d3.js canvas

755/5000 您好,我在使用d3.js库的图形时遇到问题

节点和链接(边)的位置来自后端。 我捕获了鼠标单击的位置并使其保持反应状态,然后在画布上单击时,我执行了一个名为handleClick的函数,该函数查找节点数组并比较节点的位置是否与鼠标匹配点击位置 如果万一,那么我返回该节点并将其设置为react

在画布上放大/缩小时会出现问题。并且在拖动整个图时,只要d3.zoomIdentity的变换为x:0,y:0 k:1

我分享了一小段代码

非常感谢您

let transform = d3.zoomIdentity;
const crossesGraph = useSelector(state => getCrossesSelector(state));

useEffect(() => {
    if (!loading && crossesGraph) {
      if (crossesGraph.error_message) return alert(crossesGraph.error_message);
      window.addEventListener('resize', updateWindowSize);
      // Get properties from canvas object
      const d3Canvas = d3.select('canvas');
      const canvas = d3Canvas._groups[0][0];
      // get height from the Canvas' CSS
      const styleHeight = +getComputedStyle(canvas)
        .getPropertyValue('height')
        .slice(0, -2);
      // get width from the Canvas' CSS
      const styleWidth = +getComputedStyle(canvas)
        .getPropertyValue('width')
        .slice(0, -2);
      // Fix DPI (Dots Per Pixel)
      d3Canvas
        .attr('width', styleWidth * dpi)
        .attr('height', styleHeight * dpi)
        .on('click', () => {
          // Ignore the click event if it was suppressed
          if (d3.event.defaultPrevented) {
            return;
          }
          // Extract the click location\
          const coordinates = d3.mouse(canvasRef.current);
          const x = coordinates[0];
          const y = coordinates[1];
          const pos = { x: x * dpi, y: y * dpi };
          setMousePos(pos);
        });

      const context = canvas.getContext('2d');
      const width = canvas.width;
      const height = canvas.height;
      const r = 1.5;

      //Creates a new simulation with the specified array of nodes and no forces.
      //https://github.com/d3/d3-force#forceSimulation
      //If force() is specified, assigns the force for the specified name and returns this simulation.
      //https://github.com/d3/d3-force#simulation_force
      const simulation = d3
        .forceSimulation()
        .force(
          'link',
          d3.forceLink().id(node => {
            return node.id;
          }),
        )
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter(width / 2, height / 2));

      const draw = () => {
        context.save();
        context.clearRect(0, 0, width, height);
        context.translate(transform.x, transform.y);
        context.scale(transform.k, transform.k);

        let nodeRadio = NODE_RADIO_ROOT;
        let isFirstNode = true;

        // Draw edges
        crossesGraph.links.forEach(link => {
          context.beginPath();
          context.moveTo(link.source.x, link.source.y);
          context.lineTo(link.target.x, link.target.y);
          context.lineWidth = Math.sqrt(link.value);
          context.strokeStyle = '#000';
          context.stroke();
        });
        // Draw nodes
        crossesGraph.nodes.forEach((node, i) => {
          context.beginPath();
          // Node fill
          context.moveTo(node.x + r, node.y);

          if (isFirstNode) {
            isFirstNode = false;
            // Draw circle with angles from 0 to 2 * Math.PI
            context.drawImage(
              document.getElementById(node.label),
              0,
              0,
              DEFAULT_NODE_SIZE,
              DEFAULT_NODE_SIZE,
              node.x - (nodeRadio * 2) / 2,
              node.y - (nodeRadio * 2) / 2,
              nodeRadio * 2,
              nodeRadio * 2,
            );

            context.strokeStyle = '#000';
            context.stroke();
          } else {
            nodeRadio =
              selectedNode !== null ? (selectedNode.id === node.id ? NODE_RADIO_SELECTED : NODE_RADIO) : NODE_RADIO;
            // Draw circle with angles from 0 to 2 * Math.PI
            // context.arc(node.x, node.y, nodeRadio, 0, 2 * Math.PI);
            context.drawImage(
              document.getElementById(node.label),
              0,
              0,
              DEFAULT_NODE_SIZE,
              DEFAULT_NODE_SIZE,
              node.x - (nodeRadio * 2) / 2,
              node.y - (nodeRadio * 2) / 2,
              nodeRadio * 2,
              nodeRadio * 2,
            );
          }
        });
        context.restore();
      }; //end draw

      simulation.nodes(crossesGraph.nodes).on('tick', () => {
        draw();
      });

      simulation.force('link').links(crossesGraph.links);

      const dragsubject = () => {
        let i;
        let x = transform.invertX(d3.event.x);
        let y = transform.invertY(d3.event.y);
        let dx;
        let dy;

        const nodeRadius = 3;

        for (i = crossesGraph.nodes.length - 1; i >= 0; --i) {
          let node = crossesGraph.nodes[i];
          dx = x - node.x;
          dy = y - node.y;

          if (dx * dx + dy * dy < nodeRadius * nodeRadius) {
            node.x = transform.applyX(node.x);
            node.y = transform.applyY(node.y);

            return node;
          }
        }
      };

      d3.select(canvas)
        .call(
          d3
            .drag()
            .container(canvas)
            .subject(dragsubject)
            .on('start', () => {
              if (!d3.event.active) {
                simulation.alphaTarget(0.3).restart();
              }

              d3.event.subject.fx = transform.invertX(d3.event.x);
              d3.event.subject.fy = transform.invertY(d3.event.y);
            })
            .on('drag', () => {
              d3.event.subject.fx = transform.invertX(d3.event.x);
              d3.event.subject.fy = transform.invertY(d3.event.y);
            })
            .on('end', () => {
              if (!d3.event.active) {
                simulation.alphaTarget(0);
              }
              d3.event.subject.fx = null;
              d3.event.subject.fy = null;
            }),
        )
        .call(
          d3
            .zoom()
            .scaleExtent([1, 1000])
            .on('zoom', () => {
              console.log('se ejecuta el zoomed');
              zoomed();
            }),
        );

      const zoomed = () => {
        transform = d3.event.transform;
        console.log('transform', transform)
        draw();
      };
    }
  }, [loading, crossesGraph, dpi, windowSize, selectedNode]);

const handleClick = () => {
    const nodeSelected = crossesGraph.nodes.find(node => {

      console.log('transform', transform);
      //console.log('mouse x', mousePos.x);
      console.log('node x', node.id, node.x, mousePos.x);
      return distance(mousePos.x, mousePos.y, node.x, node.y) < 6;
    });

    if (!nodeSelected) {
      return;
    }

    setSelectedNode(nodeSelected);
  };

  const updateWindowSize = () => {
    setDpi(window.devicePixelRatio);
    setwindowSize(window.innerWidth);
  };

// more code
<canvas className={classes.canvas} onClick={handleClick} ref={canvasRef}></canvas>

0 个答案:

没有答案