반응을 사용하여 d3 힘 방향 그래프를 작성하는 방법(자세한 튜토리얼)

亚连
풀어 주다: 2018-06-12 12:04:50
원래의
4458명이 탐색했습니다.

이 글은 주로 React에서 d3 force-directed graph를 작성하는 방법을 소개하고 있습니다.

D3js force-directed graph construction

d3js는 데이터를 기반으로 문서를 조작할 수 있는 JavaScript 라이브러리입니다. 데이터는 HTML, CSS, SVG 및 Canvas를 사용하여 표시할 수 있습니다. 힘 방향 그래프를 사용하여 노드 간의 다대다 관계를 나타낼 수 있습니다.

성취 효과: 연결에 화살표가 있습니다. 노드를 클릭하면 노드의 색상과 연결된 선의 두께가 변경되고 확대/축소 및 드래그할 수 있습니다.

버전: 4. 2. 코드 분해하기

1.Components

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import * as d3 from 'd3';
import { Row, Form } from 'antd';

import { chartReq} from './actionCreator';
import './Chart.less';

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

let simulation;

class Chart extends Component {
 constructor(props, context) {
  super(props, context);
  this.print = this.print.bind(this);
  this.forceChart = this.forceChart.bind(this);
  this.state = {

  };
 }

 componentWillMount() {
  this.props.dispatch(push('/Chart'));
 }

 componentDidMount() {
  this.print();
 }

 print() {
  let callback = (res) => { // callback获取后台返回的数据,并存入state
   let nodeData = res.data.nodes;
   let relationData = res.data.rels;
   this.setState({
    nodeData: res.data.nodes,
    relationData: res.data.rels,
   });
   let nodes = [];
   for (let i = 0; i < nodeData.length; i++) {
    nodes.push({
     id: (nodeData[i] && nodeData[i].id) || &#39;&#39;,
     name: (nodeData[i] && nodeData[i].name) || &#39;&#39;,
     type: (nodeData[i] && nodeData[i].type) || &#39;&#39;,
     definition: (nodeData[i] && nodeData[i].definition) || &#39;&#39;,
    });
   }
   let edges = [];
   for (let i = 0; i < relationData.length; i++) {
    edges.push({
     id: (relationData[i] && (relationData[i].id)) || &#39;&#39;,
     source: (relationData[i] && relationData[i].start.id) || &#39;&#39;,
     target: (relationData[i] && relationData[i].end.id) || &#39;&#39;,
     tag: (relationData[i] && relationData[i].name) || &#39;&#39;,
    });
   }
   this.forceChart(nodes, edges); // d3力导向图内容
  };
  this.props.dispatch(chartReq({ param: param }, callback));
 }

 // func
 forceChart(nodes, edges) {
  this.refs[&#39;theChart&#39;].innerHTML = &#39;&#39;;

  // 函数内其余代码请看拆解代码
  }

   render() {
  
    return (
     <Row style={{ minWidth: 900 }}>
      <p className="outerp">
       <p className="theChart" id="theChart" ref="theChart">
  
       </p>
      </p>
     </Row>
    );
   }
  }

  Chart.propTypes = {
   dispatch: PropTypes.func.isRequired,
  };
  
  function mapStateToProps(state) {
   return {
  
   };
  }
  
  const WrappedChart = Form.create({})(Chart);
  export default connect(mapStateToProps)(WrappedChart);
로그인 후 복사
전체 그림은 p에 그려집니다.

2. 노드 및 연결 구성

<p className="theChart" id="theChart" ref="theChart">
</p>
로그인 후 복사

구체적인 구성은 프로젝트 데이터를 기반으로 합니다.

3. 힘 모델 정의

let nodes = []; // 节点
for (let i = 0; i < nodeData.length; i++) {
  nodes.push({
    id: (nodeData[i] && nodeData[i].id) || &#39;&#39;,
    name: (nodeData[i] && nodeData[i].name) || &#39;&#39;, // 节点名称
  });
}
let edges = []; // 连线
for (let i = 0; i < relationData.length; i++) {
  edges.push({
    id: (relationData[i] && (relationData[i].id)) || &#39;&#39;,
    source: (relationData[i] && relationData[i].start.id) || &#39;&#39;, // 开始节点
    target: (relationData[i] && relationData[i].end.id) || &#39;&#39;, // 结束节点
    tag: (relationData[i] && relationData[i].name) || &#39;&#39;, // 连线名称
  });
}
로그인 후 복사

simulation.force()를 통해 힘을 설정합니다.

중심화: 중심 힘, 그래프 중심점 위치 설정 .

Collision: 노드 충돌력, .strength 매개변수 범위는 [0, 1]입니다.

링크: 연결의 힘, .distance는 연결 양쪽 끝의 노드 사이의 거리를 설정합니다.

Many-Body: .strength 매개변수가 양수이면 중력을 시뮬레이션하고, 음수이면 .distanceMax 매개변수가 최대 거리를 설정합니다.

  1. 위치 지정: 특정 방향으로 힘이 주어집니다.

    simulation.on을 통해 청력도 요소의 위치 변화를 모니터링하세요.
  2. 4. svg를 그립니다

  3. const simulation = d3.forceSimulation(nodes) // 指定被引用的nodes数组
      .force(&#39;link&#39;, d3.forceLink(edges).id(d => d.id).distance(150))
      .force(&#39;collision&#39;, d3.forceCollide(1).strength(0.1))
      .force(&#39;center&#39;, d3.forceCenter(WIDTH / 2, HEIGHT / 2))
      .force(&#39;charge&#39;, d3.forceManyBody().strength(-1000).distanceMax(800));
    로그인 후 복사
  4. svg를 만들고, svg에 g를 만들고, g에 노드 연결 및 기타 콘텐츠를 배치합니다.

  5. select: 첫 번째 해당 요소 선택

selectAll: 해당 요소 모두 선택

append: 요소 생성

5. 연결 그리기

    const svg = d3.select(&#39;#theChart&#39;).append(&#39;svg&#39;) // 在id为‘theChart&#39;的标签内创建svg
       .style(&#39;width&#39;, WIDTH)
       .style(&#39;height&#39;, HEIGHT * 0.9)
       .on(&#39;click&#39;, () => {
        console.log(&#39;click&#39;, d3.event.target.tagName);
       })
       .call(zoom); // 缩放
    const g = svg.append(&#39;g&#39;); // 则svg中创建g
    로그인 후 복사
  1. 쉘을 사용하여 선 연결 Searle 곡선 그리기: (M 시작점 배율 채우기 뷰포트

  2. 7. 노드 그리기

  3. const edgesLine = svg.select(&#39;g&#39;)
      .selectAll(&#39;line&#39;)
      .data(edges) // 绑定数据
      .enter() // 添加数据到选择集edgepath
      .append(&#39;path&#39;) // 生成折线
      .attr(&#39;d&#39;, (d) => { return d && &#39;M &#39; + d.source.x + &#39; &#39; + d.source.y + &#39; L &#39; + d.target.x + &#39; &#39; + d.target.y; }) // 遍历所有数据,d表示当前遍历到的数据,返回绘制的贝塞尔曲线
      .attr(&#39;id&#39;, (d, i) => { return i && &#39;edgepath&#39; + i; }) // 设置id,用于连线文字
      .attr(&#39;marker-end&#39;, &#39;url(#arrow)&#39;) // 根据箭头标记的id号标记箭头
      .style(&#39;stroke&#39;, &#39;#000&#39;) // 颜色
      .style(&#39;stroke-width&#39;, 1); // 粗细
    로그인 후 복사
  4. 원을 노드로 만듭니다.

    .call()은 드래그 기능을 호출합니다.

8. 노드 이름

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

텍스트가 노드의 상위 레이어에 있기 때문에 마우스 이벤트가 비활성화되지 않으면 텍스트를 클릭해도 노드 클릭 효과가 적용되지 않으며 노드를 클릭할 수 없습니다. 끌렸다.

9. 연결 이름

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

    10. 마우스를 노드로 이동하면 버블 프롬프트가 표시됩니다
  1. const nodesTexts = svg.select(&#39;g&#39;)
      .selectAll(&#39;text&#39;)
      .data(nodes)
      .enter()
      .append(&#39;text&#39;)
      .attr(&#39;dy&#39;, &#39;.3em&#39;) // 偏移量
      .attr(&#39;text-anchor&#39;, &#39;middle&#39;) // 节点名称放在圆圈中间位置
      .style(&#39;fill&#39;, &#39;black&#39;) // 颜色
      .style(&#39;pointer-events&#39;, &#39;none&#39;) // 禁止鼠标事件
      .text((d) => { // 文字内容
        return d && d.name; // 遍历nodes每一项,获取对应的name
      });
    로그인 후 복사

  2. 11. 그래프 요소의 위치 변경을 모니터링하세요. 드래그
  3. const edgesText = svg.select(&#39;g&#39;).selectAll(&#39;.edgelabel&#39;)
      .data(edges)
      .enter()
      .append(&#39;text&#39;) // 为每一条连线创建文字区域
      .attr(&#39;class&#39;, &#39;edgelabel&#39;)
      .attr(&#39;dx&#39;, 80)
      .attr(&#39;dy&#39;, 0);
    edgesText.append(&#39;textPath&#39;)// 设置文字内容
      .attr(&#39;xlink:href&#39;, (d, i) => { return i && &#39;#edgepath&#39; + i; }) // 文字布置在对应id的连线上
      .style(&#39;pointer-events&#39;, &#39;none&#39;)
      .text((d) => { return d && d.tag; });
    로그인 후 복사

  4. 13. 확대/축소

nodesCircle.append(&#39;title&#39;)
  .text((node) => { // .text设置气泡提示内容
    return node.definition;
  });
로그인 후 복사
3. 기타 효과

1. 노드 클릭 시 연결선을 굵게 만들기

simulation.on(&#39;tick&#39;, () => {
  // 更新节点坐标
  nodesCircle.attr(&#39;transform&#39;, (d) => {
    return d && &#39;translate(&#39; + d.x + &#39;,&#39; + d.y + &#39;)&#39;;
  });
  // 更新节点文字坐标
  nodesTexts.attr(&#39;transform&#39;, (d) => {
    return &#39;translate(&#39; + (d.x) + &#39;,&#39; + d.y + &#39;)&#39;;
  });
  // 更新连线位置
  edgesLine.attr(&#39;d&#39;, (d) => {
    const path = &#39;M &#39; + d.source.x + &#39; &#39; + d.source.y + &#39; L &#39; + d.target.x + &#39; &#39; + d.target.y;
    return path;
  });
  // 更新连线文字位置
  edgesText.attr(&#39;transform&#39;, (d, i) => {
    return &#39;rotate(0)&#39;;
  });
});
로그인 후 복사

2. 클릭한 노드의 색상이 변경됨

function onDragStart(d) {
  // console.log(&#39;start&#39;);
  // 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(&#39;start&#39;, onDragStart)
  .on(&#39;drag&#39;, dragging) // 拖拽过程
  .on(&#39;end&#39;, onDragEnd);
로그인 후 복사

4. 사용 시 주의사항 in React

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

그래프 구성 위치

그래프는 동적이기 때문에 여러 번 렌더링하면(렌더링을 여러 번 실행하고 여러 번 렌더링하는 경우) 대신 이전에 렌더링된 그래프를 덮어쓰지 않습니다. 여러 렌더링과 여러 그래프가 발생합니다. 구성도의 print() 함수를 componentDidMount()에 넣어서 실행시키면 한번만 렌더링됩니다.

노드 및 연결 데이터를 추가, 삭제, 수정한 후 다시 print() 함수를 호출하여 그래프를 재구성해야 합니다.

데이터를 얻을 수 있는 곳

데이터는 redux에서 얻지 않고, 요청을 보낸 후 콜백을 통해 직접 얻습니다. 5. 유용한 정보: d3 프로젝트 검색 URL

D3js 모든 프로젝트 검색 http://blockbuilder.org/search/위 내용은 앞으로 모든 사람에게 도움이 되기를 바랍니다. .

관련 기사:

vue에서 정방향 새로 고침 및 역방향 새로 고침 없음 효과를 얻는 방법

Vue2.5의 Table 및 Pagination 구성 요소를 통해 페이징 기능을 구현하는 방법

Bootstrap을 통합하는 방법 라라벨에 4개?

jquery의 select 태그에서 옵션 값을 얻는 방법

js를 사용하여 선택 옵션을 동적으로 추가하는 방법(자세한 튜토리얼)

vue.js를 사용하여 원활한 스크롤 효과를 얻는 방법

위 내용은 반응을 사용하여 d3 힘 방향 그래프를 작성하는 방법(자세한 튜토리얼)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿