Storybook中Nivo choropleth等值区域图SVG的d3 zoom事件不触发
问题根因
- 执行
d3.select('svg')的时机错误:React函数组件的逻辑执行顺序优先于DOM渲染,你在组件顶层直接选择SVG元素时,StyledResponsiveChoropleth渲染的SVG还没挂载到页面上,选中的元素为空/不符合预期,缩放事件自然没有绑定到正确的DOM上 - 缩放逻辑没有做去重处理:组件每次重渲染都会重复执行追加rect、绑定缩放事件的逻辑,会导致DOM节点冗余、事件重复绑定
- 缩放变换的应用逻辑错误:
handleZoom里每次触发缩放都新增一个g元素,没有把变换应用到Nivo渲染的实际图表容器上,就算事件触发也看不到缩放效果
修复方案
用useEffect监听组件挂载状态,等SVG渲染完成后再执行缩放初始化逻辑,同时调整变换的应用对象:
import { useEffect, useRef } from 'react' import { select, zoom, type ZoomBehavior } from 'd3' export const ChloropethChart: FC<IChoroplethChartProps> = ({ height, data, featureData, colors, domain }) => { // 存储zoom实例避免重复创建 const zoomRef = useRef<ZoomBehavior<SVGSVGElement, unknown> | null>(null) const handleZoom = (e: any) => { // 选择Nivo渲染的实际图表容器组应用变换,不要每次新增g select('svg .nivo-choropleth') .attr('transform', e.transform) } useEffect(() => { // 组件挂载后再选择SVG,此时DOM已渲染 const selectSVG = select<SVGSVGElement, unknown>('svg') if (!selectSVG.node()) return // 只追加一次事件承接层 if (!selectSVG.select('.zoom-mask').node()) { selectSVG .append('rect') .attr('class', 'zoom-mask') .attr('width', '100%') .attr('height', '100%') .style('fill', 'none') .style('pointer-events', 'all') .lower() // 放到SVG最底层,不遮挡原有图表交互 } // 初始化缩放行为 zoomRef.current = zoom<SVGSVGElement, unknown>().on('zoom', handleZoom) selectSVG.call(zoomRef.current) // 组件卸载时清理事件 return () => { if (zoomRef.current) { selectSVG.on('.zoom', null) zoomRef.current = null } } }, []) // 空依赖保证只在挂载/卸载时执行一次 return ( <StyledChoroplethChart height={height}> <StyledResponsiveChoropleth data={data} features={featureData} margin={{ top: 0, right: 0, bottom: 0, left: 0 }} colors={colors ? colors : PURPLE_RANGE} domain={domain} /> </StyledChoroplethChart> ) } export default ChloropethChart
内容的提问来源于stack exchange,提问作者Loreen
相关产品推荐
相关产品推荐

