14、构建力导向图与集成流行图表库

构建力导向图与集成流行图表库

1. 构建力导向图

力导向图是一种强大的可视化工具,用于展示节点和边之间的关系。下面将介绍如何使用 React、D3 和 TypeScript 构建一个力导向图。

1.1 数据管理

使用 Recoil 进行数据管理,设置一个选择器来获取数据。以下是 powerChartSelectors.ts 文件的代码:

// src/recoil/selectors/powerChartSelectors.ts
import { selector } from 'recoil'
import { Types } from '../../components/SimpleForceGraph/types'

export const getPowerChartData = selector({
  key: 'getPowerChartData',
  get: () => {
    return getDataFromAPI()
  },
})

const getDataFromAPI = () =>
  new Promise((resolve) =>
    fetch('/data/power_network.json').then((response) => {
      if (response.status !== 200) {
        // eslint-disable-next-line no-console
        console.log(`Houston, we have a problem! ${response.status}`)
        return
      }
      response.json().then((data) => {
        const d = data.results[0] as Types.dataObject
        resolve(d)
      })
    })
  )
1.2 网络小部件

创建一个 NetworksWidget 组件,用于集成力导向图和 Recoil 选择器。以下是 NetworksWidget.tsx 文件的代码:

// src/component/QuestionsWidget/QuestionsWidget
import React, { useState } from 'react'
import './NetworksWidget.scss'
import { useRecoilValue } from 'recoil'
import SimpleForceGraph from '../../components/SimpleForceGraph/SimpleForceGraph'
import { Types } from '../../components/SimpleForceGraph/types'
import { getPowerChartData } from '../../recoil/selectors/powerChartSelectors'

const NetworksWidget = () => {
  const forceData: Types.dataObject = useRecoilValue(getPowerChartData) as Types.dataObject
  const [selectedIndex, setSelectedIndex] = useState(0)
  return (
    <>
      {forceData ? (
        <>
          <div className="selectedText">Selected Index: {selectedIndex}</div>
          <div className="wrapperDiv">
            <SimpleForceGraph
              width={800}
              height={350}
              data={forceData}
              onNodeSelected={setSelectedIndex}
              linkDistance={80}
              linkStrength={1}
              chargeStrength={-20}
              centerWidth={350}
              centerHeight={170}
            />
          </div>
        </>
      ) : (
        <>Loading</>
      )}
    </>
  )
}

export default NetworksWidget
1.3 样式设置

设置 NetworksWidget 的样式,确保图表不溢出。以下是 NetworksWidget.scss 文件的代码:

.wrapperDiv {
  width: 800px;
  height: 350px;
  clip-path: inset(10px 20px 30px 40px);
}

.selectedText {
  font-size: 13px;
  color: #373636;
}
1.4 父组件

App.tsx 中添加 NetworksWidget 组件。以下是 App.tsx 文件的代码:

// src/App.tsx
import React from 'react'
import './App.scss'
import NetworksWidget from './widgets/NetworksWidget/NetworksWidget'

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <NetworksWidget />
      </header>
    </div>
  )
}

export default App
2. 集成流行图表库

D3 是创建图表的标准库,结合 React 可以带来更多好处。以下将介绍一些流行的 React/D3 库,并进行比较和实现。

2.1 流行的 React/D3 库

以下是一些流行的 React/D3 库:
- Recharts: https://github.com/recharts/recharts (示例: http://recharts.org/en-US/examples)
- Visx: https://github.com/airbnb/visx (示例: https://airbnb.io/visx/gallery)
- Victory: https://github.com/FormidableLabs/victory (示例: https://formidable.com/open-source/victory/gallery)
- Nivo: https://github.com/plouc/nivo (示例: https://nivo.rocks/components)
- React-vi: https://github.com/uber/react-vis (示例: https://github.com/uber/react-vis/tree/master/docs/examples/showcases)

2.2 库的比较
图表库 成本 支持 TS 模块化 模拟数据 简单性 已样式化 文档 示例 贡献者 受欢迎程度 开放问题
Recharts 432KB 👎 否 👎 177 👍 15600 👍 131
Visx 91KB 👍 否 👎 是👍 87 12500 72 👍
Victory 164KB 是 👍 135 8500 154
Nivo 241KB 是, nivo - generators 113 7800 133
React - vis 316KB 👎 部分 116 7500 277 👎
2.3 实现 Rechart

Rechart 是一个基于 React 组件的可组合图表库。以下是实现步骤:

2.3.1 安装
yarn add recharts @types/recharts
2.3.2 定义类型
// src/component/SimpleLineChart/types.ts
export namespace Types {
  export type Data = {
    date: string
    value: number
  }
}
2.3.3 实现简单折线图组件
// src/component/SimpleLineChart/SimpleLineChart.tsx
import React from 'react'
import { LineChart, Line, XAxis, CartesianGrid, Tooltip, YAxis } from 'recharts'
import { Types } from './types'

const CustomizedAxisTick = (props: { x: number; y: number; payload: { value: string } }) => {
  return (
    <g transform={`translate(${props.x},${props.y})`}>
      <text fontSize={12} x={0} y={0} dy={16} textAnchor="end" fill="black" transform="rotate(-35)">
        {props.payload.value}
      </text>
    </g>
  )
}

const CustomizedYAxisTick = (props: { x: number; y: number; payload: { value: string } }) => {
  return (
    <g transform={`translate(${props.x},${props.y})`}>
      <text fontSize="12px" x={0} y={0} dy={0} textAnchor="end" fill="black">
        {props.payload.value}
      </text>
    </g>
  )
}

const EmptyDot = () => {
  return <></>
}

const SimpleLineChart = (props: ISimpleLineChartProps) => {
  return (
    <>
      <LineChart
        width={500}
        height={300}
        data={props.data}
        margin={{
          top: 5,
          right: 30,
          left: 20,
          bottom: 5,
        }}
      >
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis
          height={60}
          dataKey="date"
          // @ts-ignore
          tick={<CustomizedAxisTick />}
        />
        <YAxis
          // @ts-ignore
          tick={<CustomizedYAxisTick />}
        />
        <Tooltip />
        <Line type="monotone" dataKey="value" stroke="#8884d8" dot={<EmptyDot />} />
      </LineChart>
    </>
  )
}

interface ISimpleLineChartProps {
  data: Types.Data[]
}

export default SimpleLineChart
2.3.4 获取数据
// src/recoil/selectors/lineDataSelectors.ts
import { selector } from 'recoil'
import * as d3 from 'd3'

export const getLineData = selector({
  key: 'getLineData',
  get: () => {
    return getData()
  },
})

const getData = () =>
  new Promise((resolve) =>
    d3
      .dsv(',', '/Data/line.csv', (d) => {
        const res = d as { date: string; data: string }
        const value = parseFloat(res.data as string)
        const { date } = res
        return {
          date,
          value,
        }
      })
      .then((data) => {
        resolve(data)
      })
  )
2.3.5 父组件
// src/App.tsx
import React from 'react'
import './App.scss'
import { useRecoilValue } from 'recoil'
import SimpleLineChart from './components/SimpleLineChart/SimpleLineChart'
import { Types } from './components/SimpleLineChart/types'
import { getLineData } from './recoil/selectors/lineDataSelectors'

function App() {
  const data = useRecoilValue(getLineData) as Types.Data[]
  return (
    <div className="App">
      <header className="App-header">
        <SimpleLineChart data={data} />
      </header>
    </div>
  )
}

export default App

通过以上步骤,你可以构建一个力导向图,并集成流行的图表库 Rechart。不同的图表库有不同的特点和适用场景,你可以根据自己的需求选择合适的库。

构建力导向图与集成流行图表库

3. 实现其他流行图表库
3.1 实现 Visx

Visx 是 Airbnb 创建的用于 React 的低级别可视化原语集合。以下是实现步骤:

步骤 1:创建新项目

yarn create react-app react-chart-libraries --template must-have-libraries
cd react-chart-libraries
yarn start
open http://localhost:3000

步骤 2:安装 Visx

yarn add @visx/visx

步骤 3:实现简单的柱状图

// src/component/SimpleBarChart/SimpleBarChart.tsx
import React from 'react';
import { Group } from '@visx/group';
import { Bar } from '@visx/shape';
import { scaleBand, scaleLinear } from '@visx/scale';

const data = [
  { key: 'A', value: 10 },
  { key: 'B', value: 20 },
  { key: 'C', value: 30 },
  { key: 'D', value: 40 },
];

const width = 500;
const height = 300;
const margin = { top: 20, right: 20, bottom: 20, left: 20 };

const xScale = scaleBand({
  range: [0, width - margin.left - margin.right],
  domain: data.map(d => d.key),
  padding: 0.2,
});

const yScale = scaleLinear({
  range: [height - margin.top - margin.bottom, 0],
  domain: [0, Math.max(...data.map(d => d.value))],
});

const SimpleBarChart = () => {
  return (
    <svg width={width} height={height}>
      <Group top={margin.top} left={margin.left}>
        {data.map((d, i) => {
          const barWidth = xScale.bandwidth();
          const barHeight = height - margin.top - margin.bottom - yScale(d.value);

          return (
            <Bar
              key={i}
              x={xScale(d.key)}
              y={yScale(d.value)}
              width={barWidth}
              height={barHeight}
              fill="#8884d8"
            />
          );
        })}
      </Group>
    </svg>
  );
};

export default SimpleBarChart;

步骤 4:父组件使用

// src/App.tsx
import React from 'react';
import './App.scss';
import SimpleBarChart from './components/SimpleBarChart/SimpleBarChart';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <SimpleBarChart />
      </header>
    </div>
  );
}

export default App;
3.2 实现 Victory

Victory 是一个用于 React 的声明式图表库。以下是实现步骤:

步骤 1:安装 Victory

yarn add victory

步骤 2:实现简单的折线图

// src/component/SimpleVictoryLineChart/SimpleVictoryLineChart.tsx
import React from 'react';
import { VictoryChart, VictoryLine } from 'victory';

const data = [
  { x: 1, y: 10 },
  { x: 2, y: 20 },
  { x: 3, y: 30 },
  { x: 4, y: 40 },
];

const SimpleVictoryLineChart = () => {
  return (
    <VictoryChart>
      <VictoryLine data={data} />
    </VictoryChart>
  );
};

export default SimpleVictoryLineChart;

步骤 3:父组件使用

// src/App.tsx
import React from 'react';
import './App.scss';
import SimpleVictoryLineChart from './components/SimpleVictoryLineChart/SimpleVictoryLineChart';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <SimpleVictoryLineChart />
      </header>
    </div>
  );
}

export default App;
3.3 实现 Nivo

Nivo 是一个提供丰富图表组件的库。以下是实现步骤:

步骤 1:安装 Nivo

yarn add @nivo/core @nivo/line

步骤 2:实现简单的折线图

// src/component/SimpleNivoLineChart/SimpleNivoLineChart.tsx
import React from 'react';
import { ResponsiveLine } from '@nivo/line';

const data = [
  {
    id: 'line',
    data: [
      { x: 'A', y: 10 },
      { x: 'B', y: 20 },
      { x: 'C', y: 30 },
      { x: 'D', y: 40 },
    ],
  },
];

const SimpleNivoLineChart = () => {
  return (
    <div style={{ height: 300 }}>
      <ResponsiveLine
        data={data}
        margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
        xScale={{ type: 'point' }}
        yScale={{ type: 'linear', min: 'auto', max: 'auto' }}
        axisTop={null}
        axisRight={null}
        axisBottom={{
          orient: 'bottom',
          tickSize: 5,
          tickPadding: 5,
          tickRotation: 0,
          legend: 'X Axis',
          legendOffset: 36,
          legendPosition: 'middle',
        }}
        axisLeft={{
          orient: 'left',
          tickSize: 5,
          tickPadding: 5,
          tickRotation: 0,
          legend: 'Y Axis',
          legendOffset: -40,
          legendPosition: 'middle',
        }}
      />
    </div>
  );
};

export default SimpleNivoLineChart;

步骤 3:父组件使用

// src/App.tsx
import React from 'react';
import './App.scss';
import SimpleNivoLineChart from './components/SimpleNivoLineChart/SimpleNivoLineChart';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <SimpleNivoLineChart />
      </header>
    </div>
  );
}

export default App;
3.4 实现 React - vis

React - vis 是 Uber 开发的用于 React 的可视化库。以下是实现步骤:

步骤 1:安装 React - vis

yarn add react-vis

步骤 2:实现简单的散点图

// src/component/SimpleScatterPlot/SimpleScatterPlot.tsx
import React from 'react';
import {
  XYPlot,
  XAxis,
  YAxis,
  VerticalGridLines,
  HorizontalGridLines,
  ScatterPlotSeries,
} from 'react-vis';

const data = [
  { x: 1, y: 10 },
  { x: 2, y: 20 },
  { x: 3, y: 30 },
  { x: 4, y: 40 },
];

const SimpleScatterPlot = () => {
  return (
    <XYPlot width={500} height={300}>
      <VerticalGridLines />
      <HorizontalGridLines />
      <XAxis />
      <YAxis />
      <ScatterPlotSeries data={data} color="#8884d8" />
    </XYPlot>
  );
};

export default SimpleScatterPlot;

步骤 3:父组件使用

// src/App.tsx
import React from 'react';
import './App.scss';
import SimpleScatterPlot from './components/SimpleScatterPlot/SimpleScatterPlot';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <SimpleScatterPlot />
      </header>
    </div>
  );
}

export default App;
4. 总结

不同的图表库各有优缺点,以下是一个简单的对比总结:

图表库 优点 缺点
Recharts 易于使用 SVG 自定义图表,图表种类多,受欢迎程度高,社区活跃 非模块化,单个图表成本较高
Visx 成本低,模块化,样式已设置好,开放问题少 学习和实现相对复杂
Victory 支持 TS,有模拟数据,实现简单 有一定的成本
Nivo 支持 TS,模块化,有模拟数据生成器,样式已设置好 实现时容器需设置宽高
React - vis 支持部分 TS,模块化,有样式设置 成本较高,受欢迎程度和支持度相对较低

在选择图表库时,需要根据项目的具体需求,如成本、复杂度、对 TypeScript 的支持等因素进行综合考虑。希望通过本文的介绍,你能更好地选择适合自己项目的图表库。

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(选择图表类型):::process
    B --> C{选择图表库}:::decision
    C -->|Recharts| D(安装并实现相应图表):::process
    C -->|Visx| E(安装并实现相应图表):::process
    C -->|Victory| F(安装并实现相应图表):::process
    C -->|Nivo| G(安装并实现相应图表):::process
    C -->|React - vis| H(安装并实现相应图表):::process
    D --> I([结束]):::startend
    E --> I
    F --> I
    G --> I
    H --> I

以上流程图展示了选择图表库并实现图表的基本流程。你可以根据自己的需求选择合适的图表库,然后按照相应的步骤进行实现。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值