问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

d3js怎么配合react使用

发布网友 发布时间:2022-05-05 02:00

我来回答

2个回答

懂视网 时间:2022-05-15 01:27

本篇文章主要介绍了如何在react中搭建d3力导向图,现在分享给大家,也给大家做个参考。

D3js力导向图搭建

d3js是一个可以基于数据来操作文档的JavaScript库。可以使用HTML,CSS,SVG以及Canvas来展示数据。力导向图能够用来表示节点间多对多的关系。

实现效果:连线有箭头,点击节点能改变该节点颜色和所连接的线的粗细,缩放、拖拽。

版本:4.X

安装和导入

npm安装:npm install d3

前端导入: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获取后台返回的数据,并存入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力导向图内容
 };
 this.props.dispatch(chartReq({ param: param }, callback));
 }

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

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

 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);

二、拆解代码

1.组件

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

整个图都将在p里绘制。

2.构造节点和连线

let nodes = []; // 节点
for (let i = 0; i < nodeData.length; i++) {
 nodes.push({
 id: (nodeData[i] && nodeData[i].id) || '',
 name: (nodeData[i] && nodeData[i].name) || '', // 节点名称
 });
}
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) || '', // 连线名称
 });
}

具体怎么构造依据你们的项目数据。

3.定义力模型

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));

通过simulation.force()设置力,可以设置这几种力:

  1. Centering:中心力,设置图中心点位置。

  2. Collision:节点碰撞作用力,.strength参数范围为[0,1]。

  3. Links:连线的作用力;.distance设置连线两端节点的距离。

  4. Many-Body:.strength的参数为正时,模拟重力,为负时,模拟电荷力;.distanceMax的参数设置最大距离。

Positioning:给定向某个方向的力。

通过simulation.on监听力图元素位置变化。

4.绘制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

创建svg,在svg里创建g,将节点连线等内容放在g内。

  1. select:选择第一个对应的元素

  2. selectAll:选择所有对应的元素

  3. append:创建元素

5.绘制连线

const edgesLine = svg.select('g')
 .selectAll('line')
 .data(edges) // 绑定数据
 .enter() // 添加数据到选择集edgepath
 .append('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 i && 'edgepath' + i; }) // 设置id,用于连线文字
 .attr('marker-end', 'url(#arrow)') // 根据箭头标记的id号标记箭头
 .style('stroke', '#000') // 颜色
 .style('stroke-width', 1); // 粗细

连线用贝塞尔曲线绘制:(M 起点X 起点y L 终点x 终点y)

6.绘制连线上的箭头

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'); // 填充颜色
  1. viewport:可视区域

  2. viewBox:实际大小,会自动缩放填充viewport

7.绘制节点

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); // 拖拽单个节点带动整个图

创建圆作为节点。

.call()调用拖拽函数。

8.节点名称

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

因为文字在节点上层,如果没有设置禁止鼠标事件,点击文字将无法响应点击节点的效果,也无法拖拽节点。

9.连线名称

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 i && '#edgepath' + i; }) // 文字布置在对应id的连线上
 .style('pointer-events', 'none')
 .text((d) => { return d && d.tag; });

10.鼠标移到节点上有气泡提示

nodesCircle.append('title')
 .text((node) => { // .text设置气泡提示内容
 return node.definition;
 });

11.监听图元素的位置变化

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)';
 });
});

12.拖拽

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);

13.缩放

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);

三、其它效果

1.单击节点时让连接线加粗

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.被点击的节点变色

nodesCircle.on('click, (node) => {
 nodesCircle.style('fill', (nodeOfSelected) => { // nodeOfSelected:所有节点, node: 选中的节点
 if (nodeOfSelected.id === node.id) { // 被点击的节点变色
 console.log('node')
 return '#36F';
 } else {
 return '#9FF';
 }
 });
})

四、在react中使用注意事项

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) || '',
 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力导向图内容
 };
 this.props.dispatch(getDataFromNeo4J({
 neo4jrun: 'match p=(()-[r]-()) return p limit 300',
 }, callback));
}

在哪里构造图 因为图是动态的,如果渲染多次(render执行多次,渲染多次),不会覆盖前面渲染的图,反而会造成渲染多次,出现多个图的现象。把构造图的函数print()放到componentDidMount()内执行,则只会渲染一次。
对节点和连线数据进行增删改操作后,需要再次调用print()函数,重新构造图。

从哪里获取数据 数据不从redux获取,发送请求后callback直接获取。

五、干货:d3项目查找网址

D3js所有项目检索.http://blockbuilder.org/search/

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中如何才能实现前进刷新后退不刷新效果

在Vue2.5中通过Table 和 Pagination 组件如何实现分页功能

在Laravel中如何整合Bootstrap 4?

在jquery中如何获取select标签中的option值

使用js如何给select动态添加option(详细教程)

使用vue.js如何实现无缝滚动效果

热心网友 时间:2022-05-14 22:35

一般js加载的元素放置在页面中的位置,有两种方法 1.一种是放在相应的容器里面,利用js动态加载进来,但是元素的位置是固定了的,就是在页面中容器原来放置在哪里,就只能放置在哪里
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
找专业防水队做完还漏水怎么维权 法院会受理房屋漏水造成的纠纷吗? 巴西龟最长活多久,家养!!! 养胃的药最好的是什么啊 婴儿积食发烧不愿吃药怎么办 板门穴位在哪个部位 手机设置放偷看的方法? 凝结水回收器生产厂家? 个人账户养老金预测公式:现有5万元,缴费20年,能领多少钱? 临沂比较有名的男装品牌 有关飞机的资料 谁知道有专门介绍飞机的网站??? 飞机的资料 comfast cf-wr360 怎么设置无线路由器 为什么打开爱奇艺网页有莫明奇妙的声音,打开别的网页没有呢 爱奇艺一起看有电流声 魅族手机怎么用爱奇艺播放器老有杂音 芭比公主之公主学校看过吗,我在手机奇艺上,看的时候有一种重复声、回声,怎么回事 爱奇艺视频总有杂音怎么办 手机汽车导航软件哪些比较好用呢 linux 上运行asp 网站,放马克斯程序(MaxCMS)可以吗? 淘宝买家从哪里能找到分销进到分销平台 华为v10Gps信号好不好 华为手机GPS信号不好是什么原因? 在淘宝开店 供货商在哪里找? linux系统上可以开发asp.net吗 如何在linux环境下搭建asp.net开发环境 我是淘宝C店的商家.想交那个分销商保障金。可是找不到那个”供应商后台“ 跪求大师 如何让linux支持ASP LINUX VPS怎么安装ASP MSSQL 现在各种已有飞机的型号,载货量及舱门大小 360网络管理前迷你无线路由器该怎么链接啊 ba169飞机 comfast WR360N如何中继别人WiFi? A320飞机导航数据库多长时间更新一次?怎么更新的? 路由器怎么设置和连接? 宫崎骏动画中的飞行器有哪些在现实中有原型? 谁能告诉我中国民航客机的飞行线路图为什么不对外公布 飞机如何更新导航数据库,国外又是怎么控制更新的 各位兄弟,你们对React.js的前景如何判断 用SQL做一个航班的数据库? react模块和react.js的区别 求列车时刻表数据库和飞机航班数据库(最好是最新的)谢谢了 我身份证资料库里面没有相片,可以坐飞机吗?急 坐飞机能带光盘吗? 求心理解释:做梦,梦见一个妖怪缠住我们全家,然后有人说去找一个很厉害的师傅。后来变成一个神秘人*扰 梦见会算命的师傅 成都出发自驾游3天最佳路线 微信红包对我国移动支付行业有什么影响 为什么好多人玩微信红包发展下线,这对他们有什么好处。。。