【React】基于Echarts实现关系图(图谱&graph)

效果

在这里插入图片描述

环境

  • echarts: ^5.5.0
  • lodash: ^4.17.21
  • next: 14.1.3
  • react: ^18

目录

仅包含涉及到的文件

| - app
   |- page.tsx
| - components
    |- echarts
       |- graph
          |- echarts.ts
          |- index.tsx

实操

创建EchartsGraph组件

  • components/echarts/graph/index.tsx
"use client"

import react, {useEffect, useRef} from "react";
import EchartsContainer from "./echarts"

type Props = {
    nodes: object[],
    edges: object[],
    categories: object[]
}

const ChartBoxStyles:react.CSSProperties = {
    width: '100%',
    height: '100%'
}

const EchartsGraph = function (props: Props) {
    let echartsRef = useRef<HTMLDivElement>(null);

    useEffect(()=>{
        const chart = EchartsContainer(echartsRef.current);
        chart.setOption({
            legend: [
                {
                    data: props.categories.map(function (a:any) {
                        return a.name;
                    })
                }
            ],
            series: [
                {
                    nodes: props.nodes,
                    edges: props.edges,
                    categories: props.categories,
                }
            ]
        });
        // 绑定鼠标松开触发事件,用于固定节点
        chart.on('mouseup', function (params: any) {
            let option = chart.getOption();
            option.series[0].nodes[params.dataIndex].x = params.event.offsetX || option.series[0].nodes[params.dataIndex].x;
            option.series[0].nodes[params.dataIndex].y = params.event.offsetY || option.series[0].nodes[params.dataIndex].y;
            option.series[0].nodes[params.dataIndex].fixed = true;
            chart.setOption(option);
        })

    }, [])

    return (
        <div ref={echartsRef} style={ChartBoxStyles}/>
    )
}

export default EchartsGraph
  • components/echarts/graph/echarts.ts
import type { EChartsType } from "echarts"
import _ from "lodash"
import * as echarts from "echarts"

class EchartsContainer {
    protected _echarts: EChartsType|null = null;
    protected _option:any = {}

    constructor(dom?:HTMLElement) {
        if (!window || !dom)throw Error('Not Set HTMLElement')

        this._echarts = echarts.init(dom);
        this._echarts?.showLoading();
        this._initOption();
    }

    private _initOption(){
        this._option.tooltip = {};
        this._option.legend = [];
        this._option.series = [
            {
                type: 'graph',
                // 布局: none、circular、force
                layout: 'force',
                // 开启动画
                animation: true,
                // 初始动画的时长
                animationDuration: 1500,
                // 数据更新动画的缓动效果
                animationEasingUpdate: 'quinticInOut',
                // 边两端的标记类型
                // @link https://echarts.apache.org/zh/option.html#series-graph.edgeSymbol
                edgeSymbol: ['arrow', ''],
                // @link https://echarts.apache.org/zh/option.html#series-graph.force
                force: {
                    // 边的两个节点之间的距离,这个距离也会受 repulsion。
                    // 值最大的边长度会趋向于 10,值最小的边长度会趋向于 50
                    edgeLength: 120,
                    // 节点之间的斥力因子
                    repulsion: 500,
                    // 节点受到的向中心的引力因子(该值越大节点越往中心点靠拢)。
                    gravity: .2,
                    // 减缓节点的移动速度(取值范围 0 到 1)
                    friction: .6
                },
                // 是否开启鼠标缩放和平移漫游
                roam: true,
                // 节点是否可拖拽
                draggable: true,
                // 高亮状态的图形样式
                // @link https://echarts.apache.org/zh/option.html#series-graph.emphasis
                // DEPRECATED: itemStyle.emphasis has been changed to emphasis.itemStyle since 4.0
                emphasis:{
                    //鼠标放上去有阴影效果
                    itemStyle: {
                        shadowColor: '#cccccc',
                        shadowOffsetX: 0,
                        shadowOffsetY: 0,
                        shadowBlur: 40,
                    },
                },
                // 文本标签配置
                // @link https://echarts.apache.org/zh/option.html#series-graph.label
                label: {
                    show: true,
                    position: 'right',
                    formatter: '{b}'
                },
                // 标签的统一布局配置
                // @link https://echarts.apache.org/zh/option.html#series-graph.labelLayout
                // labelLayout: {
                //     hideOverlap: true,
                // },
                // 针对节点之间存在多边的情况,自动计算各边曲率,默认不开启。
                // @link https://echarts.apache.org/zh/option.html#series-graph.autoCurveness
                // autoCurveness: true,
                // 滚轮缩放的极限控制
                // @link https://echarts.apache.org/zh/option.html#series-graph.scaleLimit
                scaleLimit: {
                    min: 0.4,
                    max: 2
                },
                // 关系边的公用线条样式
                // @link https://echarts.apache.org/zh/option.html#series-graph.lineStyle
                lineStyle: {
                    color: 'source',
                    // 边的曲度,支持从 0 到 1 的值,值越大曲度越大。
                    curveness: .3
                },
                categories: [],
            }
        ];

        this.setOption(this._option);
        this._echarts?.hideLoading();
    }

    public setOption(option: any){
        if (Object.keys(option).length>0){
            this._echarts?.setOption(_.merge({}, this._option, option))
        }
    }

    public getOption(){
        return this._echarts?.getOption();
    }

    public on(event: string, handler: any){
        this._echarts?.on(event, handler)
    }
}

const getSingle = (className: any) => {
    if (typeof className !== 'function') {
        throw('参数必须为一个类或一个函数')
    }
    const single = (fn: Function) => {
        let instance:any = null
        return (...args:any[]) => {
            if (!instance) {
                instance = fn.call(null, ...args)
            }
            return instance;
        }
    }
    const fn = (...args: any[]) => {
        return new className(...args);
    };
    return single(fn);
}

export default getSingle(EchartsContainer)

页面调用

demo数据源于官方的一个演示接口数据:
https://echarts.apache.org/examples/data/asset/data/les-miserables.json

import EchartsGraph from "@/components/echarts/graph";
import demoData from "./demo"

export default function Home() {

  return (
    <main style={{
        width: '100%',
        height: '100vh'
    }}>
      <EchartsGraph nodes={demoData.nodes}
                    edges={demoData.links}
                    categories={demoData.categories} />
    </main>
  );
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
首先,你需要在 React 项目中安装 echarts: ``` npm install echarts --save ``` 然后,在需要使用饼的组件中引入 echarts,并在组件的生命周期函数 `componentDidMount` 中初始化 echarts 实例,并使用数据渲染饼。 例如,以下是一个简单的饼组件的代码: ``` import React, { Component } from 'react'; import echarts from 'echarts'; class PieChart extends Component { componentDidMount() { this.initChart(); } initChart = () => { const { data } = this.props; const chart = echarts.init(this.chartRef); chart.setOption({ tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)', }, series: [ { name: '访问来源', type: 'pie', radius: ['50%', '70%'], avoidLabelOverlap: false, label: { show: false, position: 'center', }, emphasis: { label: { show: true, fontSize: '30', fontWeight: 'bold', }, }, labelLine: { show: false, }, data, }, ], }); }; render() { return ( <div ref={(ref) => { this.chartRef = ref; }} style={{ width: '100%', height: '300px' }} /> ); } } export default PieChart; ``` 在上面的代码中,我们使用 `componentDidMount` 函数初始化 echarts 实例,并使用传递进来的数据渲染饼。注意,我们在组件的 `render` 函数中返回一个 `div` 元素,这个元素的 `ref` 属性绑定了一个回调函数,用来获取这个元素的引用,以便后续使用 echarts 来渲染表。 在父组件中,我们可以使用以下代码来渲染这个饼组件: ``` import React, { Component } from 'react'; import PieChart from './PieChart'; class App extends Component { state = { data: [ { value: 335, name: '直接访问' }, { value: 310, name: '邮件营销' }, { value: 234, name: '联盟广告' }, { value: 135, name: '视频广告' }, { value: 1548, name: '搜索引擎' }, ], }; render() { const { data } = this.state; return ( <div> <PieChart data={data} /> </div> ); } } export default App; ``` 在父组件中,我们传递一个 `data` 属性给饼组件,这个属性包含了用于渲染饼的数据。最终,我们可以在页面中看到一个简单的饼

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陀螺蚁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值