" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解如何在react中搭建d3力導(dǎo)向圖

 更新時(shí)間:2022年02月17日 16:06:34   作者:互聯(lián)網(wǎng)學(xué)徒  
本篇文章主要介紹了如何在react中搭建d3力導(dǎo)向圖,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

D3js力導(dǎo)向圖搭建

d3js是一個(gè)可以基于數(shù)據(jù)來(lái)操作文檔的JavaScript庫(kù)??梢允褂肏TML,CSS,SVG以及Canvas來(lái)展示數(shù)據(jù)。力導(dǎo)向圖能夠用來(lái)表示節(jié)點(diǎn)間多對(duì)多的關(guān)系。

實(shí)現(xiàn)效果:連線有箭頭,點(diǎn)擊節(jié)點(diǎn)能改變?cè)摴?jié)點(diǎn)顏色和所連接的線的粗細(xì),縮放、拖拽。

版本:4.X

安裝和導(dǎo)入

npm安裝:npm install d3

前端導(dǎo)入:import * as d3 from 'd3';

一、完整代碼

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獲取后臺(tái)返回的數(shù)據(jù),并存入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) || '',
     name: (nodeData[i] && nodeData[i].name) || '',
     type: (nodeData[i] && nodeData[i].type) || '',
     definition: (nodeData[i] && nodeData[i].definition) || '',
    });
   }
   let edges = [];
   for (let i = 0; i < relationData.length; i++) {
    edges.push({
     id: (relationData[i] && (relationData[i].id)) || '',
     source: (relationData[i] && relationData[i].start.id) || '',
     target: (relationData[i] && relationData[i].end.id) || '',
     tag: (relationData[i] && relationData[i].name) || '',
    });
   }
   this.forceChart(nodes, edges); // d3力導(dǎo)向圖內(nèi)容
  };
  this.props.dispatch(chartReq({ param: param }, callback));
 }

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

  // 函數(shù)內(nèi)其余代碼請(qǐng)看拆解代碼
  }

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

  Chart.propTypes = {
   dispatch: PropTypes.func.isRequired,
  };
  
  function mapStateToProps(state) {
   return {
  
   };
  }
  
  const WrappedChart = Form.create({})(Chart);
  export default connect(mapStateToProps)(WrappedChart);

二、拆解代碼

1.組件

<div className="theChart" id="theChart" ref="theChart">
</div>

整個(gè)圖都將在div里繪制。

2.構(gòu)造節(jié)點(diǎn)和連線

let nodes = []; // 節(jié)點(diǎn)
for (let i = 0; i < nodeData.length; i++) {
  nodes.push({
    id: (nodeData[i] && nodeData[i].id) || '',
    name: (nodeData[i] && nodeData[i].name) || '', // 節(jié)點(diǎn)名稱
  });
}
let edges = []; // 連線
for (let i = 0; i < relationData.length; i++) {
  edges.push({
    id: (relationData[i] && (relationData[i].id)) || '',
    source: (relationData[i] && relationData[i].start.id) || '', // 開(kāi)始節(jié)點(diǎn)
    target: (relationData[i] && relationData[i].end.id) || '', // 結(jié)束節(jié)點(diǎn)
    tag: (relationData[i] && relationData[i].name) || '', // 連線名稱
  });
}

具體怎么構(gòu)造依據(jù)你們的項(xiàng)目數(shù)據(jù)。

3.定義力模型

const simulation = d3.forceSimulation(nodes) // 指定被引用的nodes數(shù)組
  .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));

通過(guò)simulation.force()設(shè)置力,可以設(shè)置這幾種力:

  1. Centering:中心力,設(shè)置圖中心點(diǎn)位置。
  2. Collision:節(jié)點(diǎn)碰撞作用力,.strength參數(shù)范圍為[0,1]。
  3. Links:連線的作用力;.distance設(shè)置連線兩端節(jié)點(diǎn)的距離。
  4. Many-Body:.strength的參數(shù)為正時(shí),模擬重力,為負(fù)時(shí),模擬電荷力;.distanceMax的參數(shù)設(shè)置最大距離。

Positioning:給定向某個(gè)方向的力。

通過(guò)simulation.on監(jiān)聽(tīng)力圖元素位置變化。

4.繪制svg

const svg = d3.select('#theChart').append('svg') // 在id為‘theChart'的標(biāo)簽內(nèi)創(chuàng)建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中創(chuàng)建g

創(chuàng)建svg,在svg里創(chuàng)建g,將節(jié)點(diǎn)連線等內(nèi)容放在g內(nèi)。

  1. select:選擇第一個(gè)對(duì)應(yīng)的元素
  2. selectAll:選擇所有對(duì)應(yīng)的元素
  3. append:創(chuàng)建元素

5.繪制連線

const edgesLine = svg.select('g')
  .selectAll('line')
  .data(edges) // 綁定數(shù)據(jù)
  .enter() // 添加數(shù)據(jù)到選擇集edgepath
  .append('path') // 生成折線
  .attr('d', (d) => { return d && 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y; }) // 遍歷所有數(shù)據(jù),d表示當(dāng)前遍歷到的數(shù)據(jù),返回繪制的貝塞爾曲線
  .attr('id', (d, i) => { return i && 'edgepath' + i; }) // 設(shè)置id,用于連線文字
  .attr('marker-end', 'url(#arrow)') // 根據(jù)箭頭標(biāo)記的id號(hào)標(biāo)記箭頭
  .style('stroke', '#000') // 顏色
  .style('stroke-width', 1); // 粗細(xì)

連線用貝塞爾曲線繪制:(M  起點(diǎn)X  起點(diǎn)y  L  終點(diǎn)x  終點(diǎn)y)

6.繪制連線上的箭頭

const defs = g.append('defs'); // defs定義可重復(fù)使用的元素
const arrowheads = defs.append('marker') // 創(chuàng)建箭頭
  .attr('id', 'arrow')
  // .attr('markerUnits', 'strokeWidth') // 設(shè)置為strokeWidth箭頭會(huì)隨著線的粗細(xì)進(jìn)行縮放
  .attr('markerUnits', 'userSpaceOnUse') // 設(shè)置為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'); // 繪制方向,可設(shè)定為:auto(自動(dòng)確認(rèn)方向)和 角度值
arrowheads.append('path')
  .attr('d', 'M0,0 L0,10 L10,5 z') // d: 路徑描述,貝塞爾曲線
  .attr('fill', '#000'); // 填充顏色
  1. viewport:可視區(qū)域
  2. viewBox:實(shí)際大小,會(huì)自動(dòng)縮放填充viewport

7.繪制節(jié)點(diǎn)

const nodesCircle = svg.select('g')
  .selectAll('circle')
  .data(nodes)
  .enter()
  .append('circle') // 創(chuàng)建圓
  .attr('r', 30) // 半徑
  .style('fill', '#9FF') // 填充顏色
  .style('stroke', '#0CF') // 邊框顏色
  .style('stroke-width', 2) // 邊框粗細(xì)
  .on('click', (node) => { // 點(diǎn)擊事件
    console.log('click');
  })
  .call(drag); // 拖拽單個(gè)節(jié)點(diǎn)帶動(dòng)整個(gè)圖

創(chuàng)建圓作為節(jié)點(diǎn)。

.call()調(diào)用拖拽函數(shù)。

8.節(jié)點(diǎn)名稱

const nodesTexts = svg.select('g')
  .selectAll('text')
  .data(nodes)
  .enter()
  .append('text')
  .attr('dy', '.3em') // 偏移量
  .attr('text-anchor', 'middle') // 節(jié)點(diǎn)名稱放在圓圈中間位置
  .style('fill', 'black') // 顏色
  .style('pointer-events', 'none') // 禁止鼠標(biāo)事件
  .text((d) => { // 文字內(nèi)容
    return d && d.name; // 遍歷nodes每一項(xiàng),獲取對(duì)應(yīng)的name
  });

因?yàn)槲淖衷诠?jié)點(diǎn)上層,如果沒(méi)有設(shè)置禁止鼠標(biāo)事件,點(diǎn)擊文字將無(wú)法響應(yīng)點(diǎn)擊節(jié)點(diǎn)的效果,也無(wú)法拖拽節(jié)點(diǎn)。

9.連線名稱

const edgesText = svg.select('g').selectAll('.edgelabel')
  .data(edges)
  .enter()
  .append('text') // 為每一條連線創(chuàng)建文字區(qū)域
  .attr('class', 'edgelabel')
  .attr('dx', 80)
  .attr('dy', 0);
edgesText.append('textPath')// 設(shè)置文字內(nèi)容
  .attr('xlink:href', (d, i) => { return i && '#edgepath' + i; }) // 文字布置在對(duì)應(yīng)id的連線上
  .style('pointer-events', 'none')
  .text((d) => { return d && d.tag; });

10.鼠標(biāo)移到節(jié)點(diǎn)上有氣泡提示

nodesCircle.append('title')
  .text((node) => { // .text設(shè)置氣泡提示內(nèi)容
    return node.definition;
  });

11.監(jiān)聽(tīng)圖元素的位置變化

simulation.on('tick', () => {
  // 更新節(jié)點(diǎn)坐標(biāo)
  nodesCircle.attr('transform', (d) => {
    return d && 'translate(' + d.x + ',' + d.y + ')';
  });
  // 更新節(jié)點(diǎn)文字坐標(biāo)
  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)';
  });
});

12.拖拽

function onDragStart(d) {
  // console.log('start');
  // console.log(d3.event.active);
  if (!d3.event.active) {
  simulation.alphaTarget(1) // 設(shè)置衰減系數(shù),對(duì)節(jié)點(diǎn)位置移動(dòng)過(guò)程的模擬,數(shù)值越高移動(dòng)越快,數(shù)值范圍[0,1]
   .restart(); // 拖拽節(jié)點(diǎn)后,重新啟動(dòng)模擬
  }
  d.fx = d.x;  // d.x是當(dāng)前位置,d.fx是靜止時(shí)位置
  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中固定的坐標(biāo)
  d.fy = null;
}
const drag = d3.drag()
  .on('start', onDragStart)
  .on('drag', dragging) // 拖拽過(guò)程
  .on('end', onDragEnd);

13.縮放

function onZoomStart(d) {
  // console.log('start zoom');
}
function zooming(d) {
  // 縮放和拖拽整個(gè)g
  // console.log('zoom ing', d3.event.transform, d3.zoomTransform(this));
  g.attr('transform', d3.event.transform); // 獲取g的縮放系數(shù)和平移的坐標(biāo)值。
}
function onZoomEnd() {
  // console.log('zoom end');
}
const zoom = d3.zoom()
  // .translateExtent([[0, 0], [WIDTH, HEIGHT]]) // 設(shè)置或獲取平移區(qū)間, 默認(rèn)為[[-∞, -∞], [+∞, +∞]]
  .scaleExtent([1 / 10, 10]) // 設(shè)置最大縮放比例
  .on('start', onZoomStart)
  .on('zoom', zooming)
  .on('end', onZoomEnd);

三、其它效果

1.單擊節(jié)點(diǎn)時(shí)讓連接線加粗

nodesCircle.on('click, (node) => {
  edges_line.style("stroke-width",function(line){
    if(line.source.name==node.name || line.target.name==node.name){
      return 4;
    }else{
      return 0.5;
    }
  });
})

2.被點(diǎn)擊的節(jié)點(diǎn)變色

nodesCircle.on('click, (node) => {
  nodesCircle.style('fill', (nodeOfSelected) => { // nodeOfSelected:所有節(jié)點(diǎn), node: 選中的節(jié)點(diǎn)
  if (nodeOfSelected.id === node.id) { // 被點(diǎn)擊的節(jié)點(diǎn)變色
    console.log('node')
      return '#36F';
    } else {
      return '#9FF';
    }
  });
})

四、在react中使用注意事項(xiàng)

componentDidMount() {
  this.print();
}
print() {
  let callback = (res) => { // callback獲取后臺(tái)返回的數(shù)據(jù),并存入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) || '',
        name: (nodeData[i] && nodeData[i].name) || '',
        type: (nodeData[i] && nodeData[i].type) || '',
        definition: (nodeData[i] && nodeData[i].definition) || '',
      });
    }
    let edges = [];
    for (let i = 0; i < relationData.length; i++) {
      edges.push({
        id: (relationData[i] && (relationData[i].id)) || '',
        source: (relationData[i] && relationData[i].start.id) || '',
        target: (relationData[i] && relationData[i].end.id) || '',
        tag: (relationData[i] && relationData[i].name) || '',
      });
    }
    this.forceChart(nodes, edges); // d3力導(dǎo)向圖內(nèi)容
  };
  this.props.dispatch(getDataFromNeo4J({
    neo4jrun: 'match p=(()-[r]-()) return p limit 300',
  }, callback));
}

在哪里構(gòu)造圖 因?yàn)閳D是動(dòng)態(tài)的,如果渲染多次(render執(zhí)行多次,渲染多次),不會(huì)覆蓋前面渲染的圖,反而會(huì)造成渲染多次,出現(xiàn)多個(gè)圖的現(xiàn)象。把構(gòu)造圖的函數(shù)print()放到componentDidMount()內(nèi)執(zhí)行,則只會(huì)渲染一次。
對(duì)節(jié)點(diǎn)和連線數(shù)據(jù)進(jìn)行增刪改操作后,需要再次調(diào)用print()函數(shù),重新構(gòu)造圖。

從哪里獲取數(shù)據(jù) 數(shù)據(jù)不從redux獲取,發(fā)送請(qǐng)求后callback直接獲取。

五、干貨:d3項(xiàng)目查找網(wǎng)址

D3js所有項(xiàng)目檢索.http://blockbuilder.org/search/

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • React DOM-diff 節(jié)點(diǎn)源碼解析

    React DOM-diff 節(jié)點(diǎn)源碼解析

    這篇文章主要為大家介紹了React DOM-diff節(jié)點(diǎn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • react swiper@6.x 工作中遇到的問(wèn)題處理方案

    react swiper@6.x 工作中遇到的問(wèn)題處理方案

    本文總結(jié)了reactswiper@6.x版本的使用方法和配置,包括安裝步驟和配置自動(dòng)輪播及移入停止的選項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • 關(guān)于react+antd樣式不生效問(wèn)題的解決方式

    關(guān)于react+antd樣式不生效問(wèn)題的解決方式

    最近本人在使用Antd開(kāi)發(fā)時(shí)遇到些問(wèn)題,所以下面這篇文章主要給大家介紹了關(guān)于react+antd樣式不生效問(wèn)題的解決方式,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • React中的Diff算法你了解嗎

    React中的Diff算法你了解嗎

    這篇文章主要為大家詳細(xì)介紹了React的Diff算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • React組件性能提升實(shí)現(xiàn)方法詳解

    React組件性能提升實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了React組件性能最佳優(yōu)化實(shí)踐分享,React組件性能優(yōu)化的核心是減少渲染真實(shí)DOM節(jié)點(diǎn)的頻率,減少Virtual?DOM比對(duì)的頻率,更多相關(guān)內(nèi)容需要的朋友可以參考一下
    2023-03-03
  • 詳解基于webpack搭建react運(yùn)行環(huán)境

    詳解基于webpack搭建react運(yùn)行環(huán)境

    本篇文章主要介紹了詳解基于webpack搭建react運(yùn)行環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • React初始化渲染過(guò)程示例詳解

    React初始化渲染過(guò)程示例詳解

    這篇文章主要為大家介紹了React初始化渲染過(guò)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • React性能優(yōu)化的實(shí)現(xiàn)方法詳解

    React性能優(yōu)化的實(shí)現(xiàn)方法詳解

    react憑借virtual DOM和diff算法擁有高效的性能,除此之外也有很多其他的方法和技巧可以進(jìn)一步提升react性能,在本文中我將列舉出可有效提升react性能的幾種方法,幫助我們改進(jìn)react代碼,提升性能
    2023-01-01
  • 詳解如何在React中有效地監(jiān)聽(tīng)鍵盤事件

    詳解如何在React中有效地監(jiān)聽(tīng)鍵盤事件

    React是一種流行的JavaScript庫(kù),用于構(gòu)建用戶界面,它提供了一種簡(jiǎn)單而靈活的方式來(lái)創(chuàng)建交互式的Web應(yīng)用程序,在React中,我們經(jīng)常需要監(jiān)聽(tīng)用戶的鍵盤事件,以便根據(jù)用戶的輸入做出相應(yīng)的反應(yīng),本文將向您介紹如何在React中有效地監(jiān)聽(tīng)鍵盤事件,并展示一些常見(jiàn)的應(yīng)用場(chǎng)景
    2023-11-11
  • React?Flux與Redux設(shè)計(jì)及使用原理

    React?Flux與Redux設(shè)計(jì)及使用原理

    這篇文章主要介紹了React?Flux與Redux設(shè)計(jì)及使用,Redux最主要是用作應(yīng)用狀態(tài)的管理。簡(jiǎn)言之,Redux用一個(gè)單獨(dú)的常量狀態(tài)樹(shù)(state對(duì)象)保存這一整個(gè)應(yīng)用的狀態(tài),這個(gè)對(duì)象不能直接被改變
    2023-03-03

最新評(píng)論

通州区| 土默特左旗| 兰考县| 巩义市| 咸阳市| 通道| 安吉县| 高台县| 乐亭县| 收藏| 淮阳县| 肥东县| 富裕县| 榆林市| 吉林省| 保康县| 江阴市| 吉木乃县| 会泽县| 青州市| 黔南| 灌阳县| 高雄县| 抚远县| 巢湖市| 龙陵县| 南岸区| 新河县| 邢台县| 南投县| 克什克腾旗| 柞水县| 和顺县| 桑日县| 苗栗市| 溆浦县| 敦化市| 莱芜市| 灯塔市| 民勤县| 揭东县|