You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过CSV设置SVG圆形的fill属性(Gatsby代码求助)

问题描述

我需要通过JavaScript根据CSV数据动态修改SVG圆形的fill属性,并实现hover时显示tooltip的功能,由于地图每日更新,必须采用动态方案。

我基于Gatsby开发,已经编写了CSV数据加载工具(useCsvData.js)和SVG修改组件(ECProjection.js),同时提供简化版SVG及gatsby-config.js配置。CSV数据可通过R处理,格式为:state id、十六进制颜色码、tooltip文本。作为JS新手,现有代码未达预期,寻求技术帮助。


现有代码

数据加载工具 useCsvData.js

// src/utils/useCsvData.js
import { useStaticQuery, graphql } from 'gatsby';
import * as d3 from 'd3';

const useCsvData = () => {
  const data = useStaticQuery(graphql`
    query {
      allStateProjCsv {
        nodes {
          state
          color
          tooltip
        }
      }
    }
  `);
  return data.allStateProjCsv.nodes;
};

export default useCsvData;

SVG修改组件 ECProjection.js

// src/components/ECProjection.js
import React, { useEffect } from 'react';
import useCsvData from '../utils/useCsvData';
import ECProjection from '../graphics/united_states.svg';
import { select } from 'd3';

const ElectoralCollegeProjection= ({ svgContent }) => {
  const data = useCsvData();

  React.useEffect(() => {
    const svg = select('svg');
    data.forEach(({ state, color, tooltip }) => {
      const circle = svg.select(`#${state}`);
      circle.attr('fill', color)
            .attr('data-tooltip', tooltip);
    });

    svg.selectAll('circle')
       .on('mouseover', function (event) {
         const tooltipText = select(this).attr('data-tooltip');
         // Show tooltip with tooltipText
       })
       .on('mouseout', function () {
         // Hide tooltip
       });
  }, [data]);

  return (
    <ECProjection />
  );
};

export default ECProjection;

SVG片段

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 930 600" preserveAspectRatio="xMidYMid meet" width="100%" height="100%">

<g class="state">
  <path class="al" d="m 643,467.4 .4,-7.3 -.9,-1.2 -1.7,-.7 -2.5,-2.8 .5,-2.9 48.8,-5.1 -.7,-2.2 -1.5,-1.5 -.5,-1.4 .6,-6.3 -2.4,-5.7 .5,-2.6 .3,-3.7 2.2,-3.8 -.2,-1.1 -1.7,-1 v -3.2 l -1.8,-1.9 -2.9,-6.1 -12.9,-45.8 -45.7,4 1.3,2 -1.3,67 4.4,33.2 .9,-.5 1.3,.1 .6,.4 .8,-.1 2,-3.8 v -2.3 l 1.1,-1.1 1.4,.5 3.4,6.4 v .9 l -3.3,2.2 3.5,-.4 4.9,-1.6 z">
  <title>Alabama</title></path>
</g>

<g class="statecircle">
  <circle id="al-circle" class="al" cx="650" cy="412" r="18" fill="#BF1D29"></circle>
  <text class="al-text" x="650" y="412" text-anchor="middle" dominant-baseline="middle" fill="white">AL</text>
</g>

gatsby-config.js

// gatsby-config.js
module.exports = {
  plugins: [
    `gatsby-transformer-csv`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/`,
        name: `data`,
      },
    },
    `gatsby-plugin-image`, //These four probably aren't relevant to our question here
    `gatsby-plugin-sharp`, // But, including just to show they are there
    `gatsby-transformer-sharp`,
    'gatsby-transformer-remark',
  ],
}

问题分析与修复方案

现有代码存在3个核心问题:

  1. 直接引入SVG作为React组件时,Gatsby会将其转换为React元素,导致D3无法准确选择DOM节点
  2. SVG中圆形ID为al-circle,但CSV中state字段是al,选择器#${state}无法匹配目标元素
  3. Tooltip仅预留注释,未实现具体显示逻辑

修复步骤

1. 安装SVG处理插件

首先安装gatsby-plugin-react-svg,确保SVG能以原始DOM结构被React引入:

npm install gatsby-plugin-react-svg

2. 更新gatsby-config.js

添加SVG插件配置:

module.exports = {
  plugins: [
    `gatsby-transformer-csv`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/`,
        name: `data`,
      },
    },
    {
      resolve: "gatsby-plugin-react-svg",
      options: {
        rule: {
          include: /graphics/ // 匹配存放SVG的目录
        }
      }
    },
    `gatsby-plugin-image`,
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
    'gatsby-transformer-remark',
  ],
}

3. 重构ECProjection.js组件

// src/components/ECProjection.js
import React, { useEffect, useRef } from 'react';
import useCsvData from '../utils/useCsvData';
import USMap from '../graphics/united_states.svg';
import { select } from 'd3';

const ElectoralCollegeProjection = () => {
  const data = useCsvData();
  const svgRef = useRef(null);

  useEffect(() => {
    if (!svgRef.current) return;

    // 创建全局Tooltip元素
    let tooltip = select('body').select('#map-tooltip');
    if (tooltip.empty()) {
      tooltip = select('body')
        .append('div')
        .attr('id', 'map-tooltip')
        .style('position', 'absolute')
        .style('background', 'rgba(0,0,0,0.8)')
        .style('color', 'white')
        .style('padding', '8px 12px')
        .style('border-radius', '4px')
        .style('pointer-events', 'none')
        .style('opacity', 0)
        .style('z-index', 1000);
    }

    const svg = select(svgRef.current);
    // 匹配CSV state与SVG圆形ID(state为al对应ID al-circle)
    data.forEach(({ state, color, tooltip: tooltipText }) => {
      const circle = svg.select(`#${state}-circle`);
      if (!circle.empty()) {
        circle.attr('fill', color)
              .attr('data-tooltip', tooltipText);
      }
    });

    // 绑定hover事件
    svg.selectAll('.statecircle circle')
       .on('mouseover', function(event) {
         const tooltipText = select(this).attr('data-tooltip');
         tooltip.html(tooltipText)
               .style('opacity', 1)
               .style('left', `${event.pageX + 10}px`)
               .style('top', `${event.pageY - 10}px`);
       })
       .on('mousemove', function(event) {
         tooltip.style('left', `${event.pageX + 10}px`)
               .style('top', `${event.pageY - 10}px`);
       })
       .on('mouseout', function() {
         tooltip.style('opacity', 0);
       });

  }, [data]);

  return (
    <USMap ref={svgRef} />
  );
};

export default ElectoralCollegeProjection;

关键修改说明

  • 使用useRef获取SVG的DOM引用,确保D3能精准操作目标元素
  • 修正选择器逻辑:CSV中的state字段对应SVG圆形ID的前缀,通过#${state}-circle匹配元素
  • 动态创建全局Tooltip,通过D3控制其显示、隐藏及跟随鼠标位置
  • 改用gatsby-plugin-react-svg引入SVG,保留原始DOM结构以便D3操作

内容的提问来源于stack exchange,提问作者Logan W.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:34:50