d3 svg 与 nextjs mouseenter 仅触发一次

问题描述 投票:0回答:1

我正在开发一个 React 组件,该组件使用了 D3,但是 svg 圆圈仅触发一次。

import React, { useEffect, useRef, useState } from 'react';
import * as d3 from 'd3';
import D3ColorLegend from './D3ColorLegend';
import {Tooltip} from './ToolTip'


const Scatterplot = ({ data, colorProperty, hoverProp }) => {
  const svgRef = useRef();
  const [details, setDetails] = useState(null);

  useEffect(() => {
    const handleMouseOver = (event, d) => {
      const [x, y] = d3.pointer(event);
      setDetails({
        xPos : x,
        yPos : y,
        name: hoverProp[data.indexOf(d)],
      });
    };
    
    // set the dimensions and margins of the graph
    const margin = { top: 10, right: 30, bottom: 30, left: 60 };
    const width = 800 - margin.left - margin.right;
    const height = 600 - margin.top - margin.bottom;

    // Find the minimum and maximum values in your data for both x and y
    const xExtent = d3.extent(data, d => d.x);
    const yExtent = d3.extent(data, d => d.y);

    // Append the SVG object to the body of the page
    const svg = d3.select(svgRef.current)
      .append('svg')
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g')
      .attr('transform', `translate(${margin.left},${margin.top})`);

    // Add X axis
    const x = d3.scaleLinear()
      .domain([Math.min(0, xExtent[0]), xExtent[1]])
      .range([0, width]);
    const xAxis = svg.append('g')
      .attr('transform', `translate(0,${height})`)
      .call(d3.axisBottom(x));

    // Add Y axis
    const y = d3.scaleLinear()
      .domain([Math.min(0, yExtent[0]), yExtent[1]])
      .range([height, 0]);
    const yAxis = svg.append('g')
      .call(d3.axisLeft(y));

    // Add a clipPath: everything out of this area won't be drawn.
    const clip = svg.append('defs').append('clipPath')
      .attr('id', 'clip')
      .append('rect')
      .attr('width', width)
      .attr('height', height)
      .attr('x', 0)
      .attr('y', 0);

    // Create the scatter variable: where both the circles and the brush take place
    const scatter = svg.append('g')
      .attr('clip-path', 'url(#clip)');

    const colorScale = d3.scaleSequential().domain([1, 10]).interpolator(d3.interpolateSinebow);

    // Add circles
    scatter
      .selectAll('circle')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', (d) => x(d.x))
      .attr('cy', (d) => y(d.y))
      .attr('r', 8)
      .style('fill', (_, i) => colorScale(colorProperty[i]))
      .style('opacity', 0.5)
      .on("mouseover", () => {} )
      .on('mousemove', (event, d) => handleMouseOver(event, d))
      .on('mouseleave', () => setDetails(null));


    // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
    const zoom = d3.zoom()
      .scaleExtent([0.5, 20]) // This controls how much you can unzoom (x0.5) and zoom (x20)
      .extent([[0, 0], [width, height]])
      .on('zoom', updateChart);

    // This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
    svg.append('rect')
      .attr('width', width)
      .attr('height', height)
      .style('fill', 'none')
      .style('pointer-events', 'all')
      .attr('transform', `translate(${margin.left},${margin.top})`)
      .call(zoom);

    // now the user can zoom, and it will trigger the function called updateChart

    // A function that updates the chart when the user zooms and thus new boundaries are available
    function updateChart(event) {
      // recover the new scale
      const newX = event.transform.rescaleX(x);
      const newY = event.transform.rescaleY(y);

      // update axes with these new boundaries
      xAxis.call(d3.axisBottom(newX));
      yAxis.call(d3.axisLeft(newY));

      // update circle position
      scatter
        .selectAll('circle')
        .attr('cx', (d) => newX(d.x))
        .attr('cy', (d) => newY(d.y));
    }
  }, [data]);

  return (
    <div className='container'>
      <D3ColorLegend />
      <div id="dataviz_axisZoom" ref={svgRef}></div>
      {details && <Tooltip interactionData={details} />}
    </div>
  );
};

export default Scatterplot;

工具提示确实渲染一次,仅此而已,所以我知道工具提示可以工作。这里有一个相关的问题,但我无法访问代码,所以如果我遇到同样的问题,我就不会访问。

有什么办法可以解决这个问题吗?

javascript reactjs d3.js next.js
1个回答
0
投票

您的大部分代码都在

useEffect()
中,它在其依赖项数组中使用
data

useEffect(() => {
..
}, [data]);

这意味着每次

useEffect()
发生变化时,
data
都会触发。如果不改变,下次就不会触发。

考虑将

handleMouseOver
函数移到
useEffect()
之外。

© www.soinside.com 2019 - 2024. All rights reserved.