当前位置:   article > 正文

D3.js力导向图+react的demo(bug修复的完整版)_react d3 关系图

react d3 关系图

原文章示例地址:
https://blog.csdn.net/weixin_33858485/article/details/91456350

效果展示
在这里插入图片描述
网上找了一圈大部分都是同一个demo,还有两个bug,一个是node节点的名称只显示了一个,另一个是连线的名称少显示了一个。

解决方案:

  1. node节点名称显示完整:
    红框部分修改,原因是前面创建连线名称时用的也是text,后面创建节点名称时取text会取到连线名称的text
    在这里插入图片描述
  2. 连线名称显示完整:
    在这里插入图片描述
    如图修改

以下是原完整代码

import React, { Component } from 'react'
import * as d3 from 'd3';

const WIDTH = 1900;
const HEIGHT = 580;
const R = 30;

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {

    }
  }
  componentDidMount() {
    this.print();
  }

  print = () => {
    //初始化数据
    const nodeData = [
      { id: 1, name: '感冒' },
      { id: 2, name: '疟疾' },
      { id: 3, name: '感冒' },
      { id: 4, name: '咽痛寒热' },
      { id: 5, name: '阴虚火旺,风邪外乘' },
      { id: 6, name: '黑膏汤' },
      { id: 7, name: '育阴之中,佐以解表' },
    ];
    let nodes = [];
    for (let i = 0; i < nodeData.length; i++) {
      nodes.push({
        id: (nodeData[i] && nodeData[i].id) || '', // 节点id
        name: (nodeData[i] && nodeData[i].name) || '', // 节点名称
      });
    }
    const relData = [
      { id: 1, source: 1, target: 2, tag: '影响2' },
      { id: 2, source: 1, target: 3, tag: '影响3' },
      { id: 3, source: 1, target: 4, tag: '影响4' },
      { id: 4, source: 1, target: 5, tag: '影响5' },
      { id: 5, source: 1, target: 6, tag: '影响6' },
      { id: 6, source: 1, target: 7, tag: '影响7' },
    ];
    let edges = [];
    for (let i = 0; i < relData.length; i++) {
      edges.push({
        id: (relData[i] && (relData[i].id)) || '', // 连线id
        source: relData[i].source, // 开始节点
        target: relData[i].target, // 结束节点
        tag: (relData[i].tag) || '', // 连线名称
      });
    }
    this.setState({
      nodes: nodes,
      edges: edges
    })
    this.forceChart(nodes, edges)
  }

  forceChart = (nodes, edges) => {
    this.refs['theChart'].innerHTML = '';

    //定义力模型
    const simulation = d3.forceSimulation(nodes) // 指定被引用的nodes数组
      .force('link', d3.forceLink(edges).id(d => d.id).distance(150))
      .force('collision', d3.forceCollide(1).strength(0.1))
      .force('center', d3.forceCenter(WIDTH / 2, HEIGHT / 2))
      .force('charge', d3.forceManyBody().strength(-1000).distanceMax(800));

    //缩放
    function onZoomStart(d) {
      // console.log('start zoom');
    }
    function zooming(d) {
      // 缩放和拖拽整个g
      // console.log('zoom ing', d3.event.transform, d3.zoomTransform(this));
      g.attr('transform', d3.event.transform); // 获取g的缩放系数和平移的坐标值。
    }
    function onZoomEnd() {
      // console.log('zoom end');
    }
    const zoom = d3.zoom()
      // .translateExtent([[0, 0], [WIDTH, HEIGHT]]) // 设置或获取平移区间, 默认为[[-∞, -∞], [+∞, +∞]]
      .scaleExtent([1 / 10, 10]) // 设置最大缩放比例
      .on('start', onZoomStart)
      .on('zoom', zooming)
      .on('end', onZoomEnd);

    //绘制svg
    const svg = d3.select('#theChart').append('svg') // 在id为‘theChart’的标签内创建svg
      .style('width', WIDTH)
      .style('height', HEIGHT * 0.9)
      .on('click', () => {
        console.log('click', d3.event.target.tagName);
      })
      .call(zoom); // 缩放
    const g = svg.append('g'); // 则svg中创建g

    //绘制连线
    const edgesLine = svg.select('g')
      .selectAll('line')
      .data(edges) // 绑定数据
      .enter() // 为数据添加对应数量的占位符
      .append('path') // 在占位符上面生成折线(用path画)
      .attr('d', (d) => { return d && 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y; }) //遍历所有数据。d表示当前遍历到的数据,返回绘制的贝塞尔曲线
      .attr('id', (d, i) => { return 'edgepath' + i; }) // 设置id,用于连线文字
      .attr('marker-end', 'url(#arrow)') // 根据箭头标记的id号标记箭头
      .style('stroke', '#000') // 颜色
      .style('stroke-width', 1); // 粗细

    //连线名称
    const edgesText = svg.select('g').selectAll('.edgelabel')
      .data(edges)
      .enter()
      .append('text') // 为每一条连线创建文字区域
      .attr('class', 'edgelabel')
      .attr('dx', 80)
      .attr('dy', 0);
    edgesText.append('textPath')
      .attr('xlink:href', (d, i) => { return '#edgepath' + i; }) // 文字布置在对应id的连线上
      .style('pointer-events', 'none') // 禁止鼠标事件
      .text((d) => { return d && d.tag; }); // 设置文字内容

    //绘制连线上的箭头
    const defs = g.append('defs'); // defs定义可重复使用的元素
    const arrowheads = defs.append('marker') // 创建箭头
      .attr('id', 'arrow')
      // .attr('markerUnits', 'strokeWidth') // 设置为strokeWidth箭头会随着线的粗细进行缩放
      .attr('markerUnits', 'userSpaceOnUse') // 设置为userSpaceOnUse箭头不受连接元素的影响
      .attr('class', 'arrowhead')
      .attr('markerWidth', 20) // viewport
      .attr('markerHeight', 20) // viewport
      .attr('viewBox', '0 0 20 20') // viewBox
      .attr('refX', 9.3 + R) // 偏离圆心距离
      .attr('refY', 5) // 偏离圆心距离
      .attr('orient', 'auto'); // 绘制方向,可设定为:auto(自动确认方向)和 角度值
    arrowheads.append('path')
      .attr('d', 'M0,0 L0,10 L10,5 z') // d: 路径描述,贝塞尔曲线
      .attr('fill', '#000'); // 填充颜色

    //拖拽
    function onDragStart(d) {
      // console.log('start');
      // console.log(d3.event.active);
      if (!d3.event.active) {
        simulation.alphaTarget(1) // 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1]
          .restart();  // 拖拽节点后,重新启动模拟
      }
      d.fx = d.x;    // d.x是当前位置,d.fx是静止时位置
      d.fy = d.y;
    }
    function dragging(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    function onDragEnd(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
      d.fx = null;       // 解除dragged中固定的坐标
      d.fy = null;
    }
    const drag = d3.drag()
      .on('start', onDragStart)
      .on('drag', dragging) // 拖拽过程
      .on('end', onDragEnd);

    //绘制节点
    const nodesCircle = svg.select('g')
      .selectAll('circle')
      .data(nodes)
      .enter()
      .append('circle') // 创建圆
      .attr('r', 30) // 半径
      .style('fill', '#9FF') // 填充颜色
      .style('stroke', '#0CF') // 边框颜色
      .style('stroke-width', 2) // 边框粗细
      .on('click', (node) => { // 点击事件
        console.log('click');
      })
      .call(drag); // 拖拽单个节点带动整个图

    //节点名称
    const nodesTexts = svg.select('g')
      .selectAll('.jdtext')
      .data(nodes)
      .enter()
      .append('text')
      .attr('class', '.jdtext')//bug修复
      .attr('dy', '.3em') // 偏移量
      .attr('text-anchor', 'middle') // 节点名称放在圆圈中间位置
      .style('fill', 'black') // 颜色
      .style('pointer-events', 'none') // 禁止鼠标事件
      .text((d) => { // 文字内容
        return d && d.name; // 遍历nodes每一项,获取对应的name
      });

    //鼠标移到节点上有气泡提示
    nodesCircle.append('title')
      .text((node) => { // .text设置气泡提示内容
        return node.name; // 气泡提示为node的名称
      });

    //监听图元素的位置变化
    simulation.on('tick', () => {
      // 更新节点坐标
      nodesCircle.attr('transform', (d) => {
        return d && 'translate(' + d.x + ',' + d.y + ')';
      });
      // 更新节点文字坐标
      nodesTexts.attr('transform', (d) => {
        return 'translate(' + (d.x) + ',' + d.y + ')';
      });
      // 更新连线位置
      edgesLine.attr('d', (d) => {
        const path = 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
        return path;
      });
      // 更新连线文字位置
      edgesText.attr('transform', (d, i) => {
        return 'rotate(0)';
      });
    });
  }

  render() {
    return (
      <div>
        <div className="outerDiv">
          <div className="theChart" id="theChart" ref="theChart">

          </div>
        </div>
      </div>
    )
  }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238

其他注意:
去掉了antd框架(没什么用)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/562996
推荐阅读
相关标签
  

闽ICP备14008679号