在 React 中使用 D3.js
问题
现在很多项目使用的是 React 或 Vue,而 D3.js 却是直接操作 DOM 的,所以想在这些框架中使用 D3.js 不能使用一般的方式。下文以一个饼图为例介绍两种不错的方案。
方案 1: D3.js
做计算
一种方案是只使用 D3.js 做计算,使用 React 根据计算值生成 DOM。
import * as React from 'react';
import * as d3 from 'd3';
import { PieArcDatum } from 'd3';
import { str2rgb } from 'utils';
interface PositionData {
symbol: string;
val: number;
}
interface PositionsPieProps {
positions: PositionData[];
width: number;
height: number;
}
const PositionsPie = (props: PositionsPieProps) => {
const { positions, width, height } = props;
const radius = Math.min(width, height) / 2;
const pie = d3
.pie<PositionData>().sort(null)
.value(d => d.val)(positions);
const arcPathGen = d3.arc<PieArcDatum<PositionData>>()
.innerRadius(0)
.outerRadius(radius - 10);
return (
<svg width={width} height={height}>
<g transform={`translate(${width / 2}, ${height / 2})`}>
{
pie.map((val, idx) => {
const arcPath = arcPathGen(val) as string;
const labelPath = d3.arc<PieArcDatum<PositionData>>()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
return (
<g key={idx}>
<path d={arcPath} fill={str2rgb(val.data.symbol)} />
<text
dy="0.35em"
style={{ textAnchor: 'middle' }}
transform={`translate(${labelPath.centroid(val)})`}
>
{val.data.symbol}
</text>
</g>
);
})
}
</g>
</svg>
);
};
export default PositionsPie;
d3.pie()
与 d3.arc()
都可以用来做计算操作,生成绘制 SVG 所用的数据。
方案 2: D3.js 操作类 DOM 结构
react-faux-dom 这个库可以生成类似 DOM 的数据结构来给 D3.js 操作,然后渲染成 React 元素。
const PositionsPie = (props: PositionsPieProps) => {
const { positions, width, height } = props;
const radius = Math.min(width, height) / 2;
const el = ReactFauxDOM.createElement('svg');
const svg = d3.select(el);
svg.attr('width', width).attr('height', height);
const g = svg.append('g');
g.attr('transform', `translate(${width / 2}, ${height / 2})`);
const pie = d3.pie<PositionData>().sort(null)
.value(d => d.val);
const path = d3.arc<PieArcDatum<PositionData>>()
.innerRadius(0)
.outerRadius(radius - 10);
const label = d3.arc<PieArcDatum<PositionData>>()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
const arc = g.selectAll('.arc')
.data(pie(positions))
.enter().append('g')
.attr('class', 'arc');
arc.append('path')
.attr('d', path)
.attr('fill', d => str2rgb(d.data.symbol));
arc.append('text')
.attr('transform', d => `translate(${label.centroid(d)})`)
.attr('dy', '0.35em')
.attr('style', 'textAnchor: middle')
.text(d => d.data.symbol);
return el.toReact();
};
除了能够像 D3.js 默认的方式那样进行操作外,还支持动画。