HTML实现的d3连续图例转React时scaleSequential方法不存在如何解决
D3连续色阶图例转React组件解决方案
报错原因
该报错核心诱因有三点:
react-d3-library仅为React与D3的适配封装库,未包含D3核心方法与d3-scale-chromatic的色阶插值能力- 通过react-helmet加载CDN脚本的时机晚于组件逻辑执行时间,D3实例未挂载完成就调用方法自然报错
- 直接通过ID选择DOM、将普通函数返回值当作React组件渲染的写法也不符合React开发规范
推荐解决方案(npm依赖安装,无需CDN)
你不需要找d3-scale-chromatic对应的React封装,原生D3库可以直接在React中使用,操作步骤如下:
- 先卸载无效依赖、安装正式D3相关包
npm uninstall react-d3-library npm install d3 d3-scale-chromatic
- 替换你的组件代码如下:
import React, { useRef, useEffect } from "react"; import * as d3 from "d3"; import * as d3Chromatic from "d3-scale-chromatic"; // 封装为独立的连续色阶图例组件,可复用 function ContinuousLegend() { // 用useRef绑定DOM容器,避免全局ID冲突 const legendRef = useRef(null); // 保证DOM渲染完成后再执行D3绘制逻辑 useEffect(() => { if (!legendRef.current) return; // 清空之前的渲染内容,避免组件重渲染时重复生成图例 d3.select(legendRef.current).selectAll("*").remove(); // 初始化色阶 const colorScale1 = d3.scaleSequential(d3Chromatic.interpolateYlGnBu) .domain([-4, 16]); // 调用绘制方法 continuous(legendRef.current, colorScale1); }, []); function continuous(container, colorscale) { const legendheight = 200, legendwidth = 80, margin = {top: 10, right: 60, bottom: 10, left: 2}; const canvas = d3.select(container) .style("height", legendheight + "px") .style("width", legendwidth + "px") .style("position", "relative") .append("canvas") .attr("height", legendheight - margin.top - margin.bottom) .attr("width", 1) .style("height", (legendheight - margin.top - margin.bottom) + "px") .style("width", (legendwidth - margin.left - margin.right) + "px") .style("border", "1px solid #000") .style("position", "absolute") .style("top", (margin.top) + "px") .style("left", (margin.left) + "px") .node(); const ctx = canvas.getContext("2d"); const legendscale = d3.scaleLinear() .range([1, legendheight - margin.top - margin.bottom]) .domain(colorscale.domain()); const image = ctx.createImageData(1, legendheight); d3.range(legendheight).forEach(function(i) { const c = d3.rgb(colorscale(legendscale.invert(i))); image.data[4*i] = c.r; image.data[4*i + 1] = c.g; image.data[4*i + 2] = c.b; image.data[4*i + 3] = 255; }); ctx.putImageData(image, 0, 0); const legendaxis = d3.axisRight() .scale(legendscale) .tickSize(6) .ticks(8); const svg = d3.select(container) .append("svg") .attr("height", (legendheight) + "px") .attr("width", (legendwidth) + "px") .style("position", "absolute") .style("left", "0px") .style("top", "0px") svg .append("g") .attr("class", "axis") .attr("transform", "translate(" + (legendwidth - margin.left - margin.right + 3) + "," + (margin.top) + ")") .call(legendaxis); } return <div ref={legendRef} style={{display: "inline-block"}}></div>; } function App() { return ( <div style={{margin: "30px"}}> <ContinuousLegend /> </div> ); } export default App;
可选CDN方案(不推荐)
如果坚持要通过CDN加载D3,不要用react-helmet动态注入,直接在项目的public/index.html的<head>标签中提前加入CDN链接:
<script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
组件中直接使用全局变量window.d3调用相关方法即可,无需安装npm的D3依赖。
内容的提问来源于stack exchange,提问作者mmz
相关产品推荐
相关产品推荐

