向 React 和 D3.js 中的密度图添加工具提示

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

我正在尝试使用 React 和 D3.js 构建密度图。

我希望用户能够将鼠标悬停在图表的任何部分上,并看到显示有关该特定点的附加信息的工具提示,但我不确定如何将工具提示添加到路径元素。我找不到任何相关的例子。有人做过类似的事情吗?

代码:https://playcode.io/1640953

import {useMemo, useRef } from "react";
import * as d3 from "d3";

import  AxisBottom  from "./axisBottom";
import  AxisLeft  from "./axisTop";
import importedData from "./data"

const MARGIN = { top: 50, right: 30, bottom: 50, left: 50 };


export const DensityChart = ({ width = 700, height = 400}) => {
  const boundsWidth = width - MARGIN.right - MARGIN.left;
  const boundsHeight = height - MARGIN.top - MARGIN.bottom;

  const refs = useRef({
    dataMin: Math.min(...importedData),
    dataMax: Math.max(...importedData),
    domain: [Math.min(...importedData), Math.max(...importedData)]
  })

  const xScale = useMemo(() => {
    try {
      const result = d3.scaleLinear().domain([refs.current.dataMin, refs.current.dataMax]).range([10, boundsWidth - 10]);
      return result
    } catch (error) {
      console.error("Error creating xScale:", error);
      return d3.scaleLinear().domain([0, 1]).range([10, boundsWidth - 10]);
    }
  }, [importedData, width]);

    // Compute kernel density estimation
    const density = useMemo(() => {
      const kde = kernelDensityEstimator(kernelEpanechnikov(7), xScale.ticks(40));
      return kde(importedData);
    }, [xScale, importedData]);

  const yScale = useMemo(() => {
    try {
      const max = Math.max(...density.map((d) => d[1]));
      const result = d3.scaleLinear().range([boundsHeight, 0]).domain([0, max])
      return result;
    } catch (error) {
      console.error("Error creating yScale:", error);
      return
    }
  }, [importedData, height, density]);

  const path = useMemo(() => {
    try {
      const lineGenerator = d3.line().x((d) => {
       return xScale(d[0])
      })
      .y((d) => {
        return yScale(d[1])
      })
      .curve(d3.curveBasis);
      return lineGenerator(density);
    } catch (error) {
      console.error("Error creating path:", error);
      return "";
    }
  }, [density, xScale, yScale]);


  return (
    <section>
    <svg height="500" width="700">
      <g
        width={boundsWidth}
        height={boundsHeight}
        transform={`translate(${[MARGIN.left, MARGIN.top].join(",")})`}
      >
       
        <path
          d={path}
          fill="blue"
          opacity={0.4}
          stroke="black"
          strokeWidth={1}
          strokeLinejoin="round"
        />
        <g transform={`translate(0, ${boundsHeight})`}>
          <AxisLeft yScale={yScale} pixelsPerTick={40} />
        </g>
        <g transform={`translate(0, ${boundsHeight})`}>
          <AxisBottom xScale={xScale} pixelsPerTick={40} />
        </g>

      </g>
    </svg>
    </section>
  );
}

export default DensityChart;

// Function to compute density
function kernelDensityEstimator(kernel, X) {
  return function(V) {
    return X.map(function(x) {
      return [x, d3.mean(V, function(v) { return kernel(x - +v )})];
    });
  };
}
function kernelEpanechnikov(bandwidth) {
  return function(v) {
    const result = Math.abs(v /= bandwidth) <= 1 ? 0.75 * (1 - v * v) / bandwidth : 0;
    return result
  };
}
javascript reactjs d3.js visualization
1个回答
0
投票

实际上你的问题与 d3 没有任何关系。用普通的javascript就可以解决。 SVG 路径通常仍然尊重指针事件(只要它们具有显式定义的填充颜色或指针事件)

例如使用 css 使路径部分改变颜色:

path {
    fill:blue;
    stroke:black;
}
path:hover{
    fill:red;
    stroke:blue;
}
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
 <g>
  <title>Layer 1</title>
  <path id="svg_3" d="m200,114l-114,95l26,97l99,20c0,0 19,-67 19,-68c0,-1 -1,-5 4,-8c5,-3 39,-10 40,-10c1,0 13,-2 14,-9c1,-7 -4,-36 -8,-40c-4,-4 -23,-15 -27,-17c-4,-2 -24,-16 -24,-23c0,-7 -1,-15 -1,-21c0,-6 -6,-19 -7,-19c-1,0 -21,3 -21,3z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
  <path id="svg_4" d="m244,101c0,0 14,55 16,56c2,1 22,11 24,13c2,2 23,17 24,18c1,1 1,28 -1,31c-2,3 -3,25 -9,30c-6,5 -32,14 -35,14c-3,0 -8,5 -10,8c-2,3 -10,37 -10,37c0,0 7,10 16,15c9,5 53,12 59,12c6,0 30,0 40,-8c10,-8 34,-7 35,-31c1,-24 1,-48 1,-65c0,-17 -13,-61 -15,-66c-2,-5 -21,-21 -21,-23c0,-2 34,-20 44,-15c10,5 29,24 33,28c4,4 10,20 16,5c6,-15 28,-31 -1,-46c-29,-15 -25,-24 -55,-25c-30,-1 -42,-5 -53,-5c-11,0 -46,-2 -52,1c-6,3 -46,16 -46,16z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
  <path id="svg_5" d="m428,180c0,0 -3,52 -4,53c-1,1 -2,27 -2,31c0,4 -8,29 -11,34c-3,5 -15,36 -21,38c-6,2 -77,18 -81,18c-4,0 -68,0 -68,7c0,7 -1,18 8,23c9,5 23,9 45,14c22,5 97,12 111,6c14,-6 44,-20 55,-30c11,-10 28,-28 37,-42c9,-14 14,-15 23,-40c9,-25 16,-109 12,-114c-4,-5 -32,-12 -45,-9c-13,3 -59,11 -59,11z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
 </g>
</svg>

您可以用同样的方式添加 javascript 事件:

Array
  .from(document.querySelectorAll("path"))
  .forEach((el) => {
    el.onmouseover = () => console.log("Mouse Over " + el.id);
  })
path {
    fill:blue;
    stroke:black;
}
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
 <g>
  <path id="svg_3" d="m200,114l-114,95l26,97l99,20c0,0 19,-67 19,-68c0,-1 -1,-5 4,-8c5,-3 39,-10 40,-10c1,0 13,-2 14,-9c1,-7 -4,-36 -8,-40c-4,-4 -23,-15 -27,-17c-4,-2 -24,-16 -24,-23c0,-7 -1,-15 -1,-21c0,-6 -6,-19 -7,-19c-1,0 -21,3 -21,3z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
  <path id="svg_4" d="m244,101c0,0 14,55 16,56c2,1 22,11 24,13c2,2 23,17 24,18c1,1 1,28 -1,31c-2,3 -3,25 -9,30c-6,5 -32,14 -35,14c-3,0 -8,5 -10,8c-2,3 -10,37 -10,37c0,0 7,10 16,15c9,5 53,12 59,12c6,0 30,0 40,-8c10,-8 34,-7 35,-31c1,-24 1,-48 1,-65c0,-17 -13,-61 -15,-66c-2,-5 -21,-21 -21,-23c0,-2 34,-20 44,-15c10,5 29,24 33,28c4,4 10,20 16,5c6,-15 28,-31 -1,-46c-29,-15 -25,-24 -55,-25c-30,-1 -42,-5 -53,-5c-11,0 -46,-2 -52,1c-6,3 -46,16 -46,16z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
  <path id="svg_5" d="m428,180c0,0 -3,52 -4,53c-1,1 -2,27 -2,31c0,4 -8,29 -11,34c-3,5 -15,36 -21,38c-6,2 -77,18 -81,18c-4,0 -68,0 -68,7c0,7 -1,18 8,23c9,5 23,9 45,14c22,5 97,12 111,6c14,-6 44,-20 55,-30c11,-10 28,-28 37,-42c9,-14 14,-15 23,-40c9,-25 16,-109 12,-114c-4,-5 -32,-12 -45,-9c-13,3 -59,11 -59,11z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="5" stroke="#000000" fill="none"/>
 </g>
</svg>

因此,您可以使用简单的 css 来显示工具提示,甚至可以使用 javascript 来显示更多自定义工具提示行为。

.tooltip {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted black;
}

.tooltip .tooltiptext {
  visibility: hidden;
  width: 120px;
  background-color: #555;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  position: absolute;
  z-index: 1;
  top: 125%;
  left: 50%;
  margin-left: -60px;
  opacity: 0;
  transition: opacity 0.3s;
}

.tooltip:hover .tooltiptext {
  visibility: visible;
  opacity: 1;
}
<div class="tooltip">Hover over me
  <span class="tooltiptext">Tooltip text</span>
</div>

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