React 项目中使用 dagre-d3

栏目: IOS · Android · 发布时间: 6年前

内容简介:React 已经成为了公司所有项目的前端必选框架了,参考了一下第一步很重要,我们图当然是要绘制在DOM上了

React 已经成为了公司所有项目的前端必选框架了, JavaScript 社区非常活跃,而 React 也几乎成为了前端最火热的框架,所以各种知名的库几乎都已经有了 React 的版本。但是一些库还没有,比如 dagre-d3dagre-d3 是一个来绘制关系图的 Javascript 库。

参考了一下 react-highcharts ,其实在 React 中实现 dagre-d3 其实也还好。

  • 获取DOM node

第一步很重要,我们图当然是要绘制在DOM上了

ref 简介

Refs and the DOM

React提供的 ref 属性,表示为对组件真正实例的引用,其实就是ReactDOM.render()返回的组件实例。 有一点要注意一下, ReactDOM.render() 渲染组件时返回的是组件实例;而渲染dom元素时,返回是具体的dom节点。

getDOMRef(ref) {
  console.log(ref);
}

getComponentRef(ref) {
  console.log(ref);
}

<svg ref={this.getDOMRef} />

<DagreD3 ref={this.getComponentRef} />
复制代码
React 项目中使用 dagre-d3

这里我们用nodeTree和nodeTreeInner来保存具体的dom节点。

setNodeTree = (nodeTree) => {
  this.nodeTree = nodeTree;
}

setNodeTreeInner = (nodeTreeInner) => {
  this.nodeTreeInner = nodeTreeInner;
}

render() {
  return (
    <svg ref={this.setNodeTree}>
      <g ref={this.setNodeTreeInner} />
    </svg>
  )
}
复制代码
  • 渲染到DOM上(开始画图)

由于要获取真正的 DOM 节点,所以需要将上面代码的执行放到 componentDidMount

我们定义一个 renderDag 方法,放到 componentDidMountcomponentDidUpdate 里面

renderDag() {
  const { nodes, edges } = this.props;
  const g = new dagreD3.graphlib.Graph()
    .setGraph({}) // Set an object for the graph label
    .setDefaultNodeLabel(() => ({}))
    .setDefaultEdgeLabel(() => ({})); // Default to assigning a new object as a label for each new edge.
  Object.keys(nodes).forEach((id) => { // 画点
    g.setNode(id, nodes[id]);
  });
  edges.forEach((edge) => { // 画线
    edge[2] ? g.setEdge(edge[0], edge[1], edge[2]) : g.setEdge(edge[0], edge[1]);
  });
  
  // 渲染dag图
  const svg = d3.select(this.nodeTree);
  const inner = d3.select(this.nodeTreeInner);
  // 鼠标滚动缩放
  const zoom = d3.zoom().on('zoom', () => inner.attr('transform', d3.event.transform));
  svg.call(zoom);

  const render = new dagreD3.render(); // eslint-disable-line

  render(inner, g);
}

复制代码
  • 效果图
React 项目中使用 dagre-d3
  • 完整代码
import React from 'react';
import PropTypes from 'prop-types';
import * as dagreD3 from 'dagre-d3';
import * as d3 from 'd3';

class DagreD3 extends React.Component {
  static defaultProps = {
    width: '100%',
    height: '100%',
    nodes: {},
    edges: [],
    graph: {},
    interactive: false,
    onNodeClick: () => {}
  }

  static propTypes = {
    width: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    height: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    nodes: PropTypes.object,
    edges: PropTypes.array,
    graph: PropTypes.object,
    interactive: PropTypes.bool,
    onNodeClick: PropTypes.func,
    onNodeHover: PropTypes.func
  }

  componentDidMount() {
    this.renderDag();
  }

  shouldComponentUpdate(nextProps) {
    return !(this.props.nodes === nextProps.nodes)
      || !(this.props.edges === nextProps.edges);
  }

  componentDidUpdate() {
    this.renderDag();
  }

  setNodeTree = (nodeTree) => {
    this.nodeTree = nodeTree;
  }

  setNodeTreeInner = (nodeTreeInner) => {
    this.nodeTreeInner = nodeTreeInner;
  }

  renderDag() {
    const { nodes, edges, interactive, fit, onNodeClick, graph } = this.props;
    const g = new dagreD3.graphlib.Graph()
      .setGraph({ ...graph }) // Set an object for the graph label
      .setDefaultNodeLabel(() => ({}))
      .setDefaultEdgeLabel(() => ({})); // Default to assigning a new object as a label for each new edge.
    Object.keys(nodes).forEach((id) => {
      g.setNode(id, nodes[id]);
    });
    edges.forEach((edge) => {
      edge[2] ? g.setEdge(edge[0], edge[1], edge[2]) : g.setEdge(edge[0], edge[1]);
    });

    const svg = d3.select(this.nodeTree);
    const inner = d3.select(this.nodeTreeInner);
    if (interactive) { // 自适应缩放
      const zoom = d3.zoom().on('zoom', () => inner.attr('transform', d3.event.transform));
      svg.call(zoom);
    }
    const render = new dagreD3.render(); // eslint-disable-line

    render(inner, g);


    // Run the renderer. This is what draws the final graph.
    render(inner, g);

    // 自适应宽高
    if (fit) {
      const { height: gHeight, width: gWidth } = g.graph();
      const { height, width } = this.nodeTree.getBBox();
      const transX = width - gWidth;
      const transY = height - gHeight;
      svg.attr('viewBox', `0 0 ${width} ${height}`);
      inner.attr('transform', d3.zoomIdentity.translate(transX, transY));
    }

    if (onNodeClick) { // 点击事件
      svg.selectAll('g.node').on('click',
        id => onNodeClick(id));
    }
  }

  render() {
    const { width, height } = this.props;
    return (
      <svg width={width} height={height} ref={this.setNodeTree}>
        <g ref={this.setNodeTreeInner} />
      </svg>
    );
  }
}

export { d3 };

export default DagreD3;
复制代码
  • 使用
<DagreD3
  fit
  interactive
  graph={{ rankdir: 'LR' }}
  nodes={nodes}
  edges={edges}
  onNodeClick={this.onNodeClick}
/>
复制代码

以上所述就是小编给大家介绍的《React 项目中使用 dagre-d3》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

设计模式之禅

设计模式之禅

秦小波 / 机械工业出版社 / 2010年3月 / 69.00元

如果说“四人帮”的《设计模式》是设计模式领域的“圣经”,那么之后出版的各种关于设计模式的书都可称之为“圣经”的“注释版”或“圣经的故事”。本书是得道者对“圣经”的“禅悟”,它既不像“圣经”那样因为惜字如金、字字珠玑而深奥、晦涩和难懂,又比“圣经”的“注释版”更深刻和全面、更通俗和生动、更接近开发者遇到的实践场景,更具指导性。本书兼收并蓄、博采众长,也许是设计模式领域里的下一个里程碑之作。 全......一起来看看 《设计模式之禅》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码