如何通过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个核心问题:
- 直接引入SVG作为React组件时,Gatsby会将其转换为React元素,导致D3无法准确选择DOM节点
- SVG中圆形ID为
al-circle,但CSV中state字段是al,选择器#${state}无法匹配目标元素 - 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.
相关产品推荐
相关产品推荐

