赞
踩
3d-force-graph 一个生成力导图的插件,但是api文档不是很友好,网上教程也很少几乎找不到,写完一个例子所以记录一下自己用到的一些api的配置使用,最开始看官方例子最简单,建议初学者先看官方例子熟悉配置,再参考这边的配置,最后深入可以研究threejs结合起来可扩展性还是很强的
3d力导图3d-force-graph github地址
使用ThreeJS / WebGL进行3D渲染,使用d3-force-3d或ngraph作为底层物理引擎。
它要使用需要对THREE.js有基本的了解
注意:
它的最新版有所不同,以前的bug也改进了,以前graph.refresh()有这个方法但是更新有问题,修改nodeOpacity等反而可以触发更新。现在.refresh()可以更新整个图谱,修改node属性只能更新节点。包括新增了一些方法。
本文档不保证适合最新版,最好的办法是上手并查阅官方文档,英语不好翻译软件翻译一下也够用了
script可直接引入,npm 可通过npm install --save 3d-force-graph安装再通过import引入。我试了几台电脑npm 都会报错不能引入,cnpm就没有问题.。但是cnpm引入three可能会出问题,最好使用npm
第一次接触最好看官方github上的例子开始着手,上手熟悉属性,后续再根据需求和官方文档开发
vue 中的使用
initGraph(){ this.elem = document.getElementById('3d-graph'); let WIDTH = document.getElementById('3d-graph').clientWidth; let HEIGHT = document.getElementById('3d-graph').clientHeight; let colors = { //定义数组,方便操作节点颜色。例初始随机色,点击节点,节点及其相连节点变亮,其他节点变暗,可查看官方例子,有专门的讲这一点的 caNodes : [], mainLinks : [], }; this.Graph = ForceGraph3D() (this.elem) //数据格式{nodes:[{id:1,..},{id:2,..}...],links:[{source:1,target:2,..}...]} // .jsonUrl('../datasets/block.json') //引入json文件数据 .graphData(this.graphData) //引入其他数据,更新数据也可用this.Graph.graphData(this.graphData) .backgroundColor('rgba(0,0,0,0)') .nodeLabel((node)=>{ //鼠标移上节点展示数据 return node.id; //可采用`<p style="">${node.name}<p>`dom元素模式 }) .nodeRelSize(4); //可用于更新数据和修改节点大小,只能存数字,需要用方法可以使用nodeVal() .linkLabel((link)=>{//鼠标移上连线展示信息 // console.log(link); let label = ''; return label; }) // .nodeAutoColorBy('id') //节点随机色 .nodeColor((node)=>{ //自定义色 }) // .nodeOpacity(0.75) //节点透明度 .linkColor((link)=>{ //连线颜色 }) .linkWidth((link)=>colors.mainLinks.indexOf(link)!=-1 ? 3 : 1) //如果是主连线,其宽度为3否则为1 .width(WIDTH) .height(HEIGHT) .onNodeHover(node => this.elem.style.cursor = node ? 'pointer' : null) .onNodeClick(node => { // this.handleCamera(node); 相机操作,比如拉近 this.Graph.nodeRelSize(4);//(最新版用refresh更新)修改节点大小/半径,并更新。如果修改了颜色用这个方法可以触发更新才能看到效果, }) .onLinkHover(link => this.elem.style.cursor = link ? 'pointer' : null) .onLinkClick(link=>{ }); //修改节点连线长度,同d3引擎用法,如果要调整物理引擎需要对d3有一定了解再参考官方提供的配置项 this.Graph.d3Force('link').distance(this.space); },
初始化之后会遇到不同的需求去修改初始化后的属性
// 调整拉近相机,官方例子上有,修改distance大小即修改相机拉近距离 handleCamera(node){ const distance = 600; const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z); this.Graph.cameraPosition( { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position node, // lookAt ({ x, y, z }) 3000 // ms transition duration ); }, // 播放/暂停 runCanvas(){ this.run = !this.run; !this.run?this.Graph.pauseAnimation():this.Graph.resumeAnimation(); },
添加其他three模型进入3d-force-graph场景,要先安装three再引入
npm install three -S
import * as THREE from 'three'
//或者
var THREE = require("three");
再引入相关的渲染插件。一般可以不通过npm 安装,直接放入文件夹再引入。如此处节点上显示2d字体
import CSS2DObject,CSS2DRenderer} from '../utils/THREE/CSS2DRenderer.js render() { //两个render需要对three.js有一定了解 requestAnimationFrame(this.render); this.twoDRenderer.render(this.Graph.scene(), this.Graph.camera()); }, render2D() { //CSS2DRenderer let a = document.getElementById("3d-graph"); this.twoDRenderer = new THREE.CSS2DRenderer(); this.twoDRenderer.setSize(a.clientWidth, a.clientWidth); this.twoDRenderer.domElement.style.position = "absolute"; this.twoDRenderer.domElement.style.top = "0px"; this.twoDRenderer.domElement.style.pointerEvents= "none"; //this.twoDRenderer.domElement.style.zIndex = -1; //一般情况下2d字体在3d-graph下面是有一个div放置的,可能会出现看不到的情况, //调整z-index和样式或者直接加pointerEvents:none就可以解决,每个人遇到的情况都不同,比如我用z-index有些地方这样写可行有的就不行。 //还有如果使用3drenderer其本质还是dom,但是3drenderer会存在景深层级比z-index更高,这种情况下去掉景深perspective,z-index:-1可以正常,但是dom不会随控制器缩放而缩放。可以给canvas一个样式pointer-events:none,使canvas可穿透,但是canvas点击事件会失效,可以尝试取消dom的点击事件,canvas即可正常(提供一种解决方案,具体视情况而定) a.appendChild(this.twoDRenderer.domElement); this.twoDRenderer.render(this.Graph.scene(), this.Graph.camera()); /* let grid = new THREE.GridHelper(1000, 50, 0x64FE00, 0x0C291F); //网格辅助,大小,行距,中心线颜色,网格线条颜色 this.Graph.scene().add(grid); */ },
如果需要在原有力导图的基础上,添加别的模型或者需要将球体换成别的自定义three模型。例如原api没有提供的长显示文字信息,只有鼠标移上才能显示相关信息。可以调用上面的创建2d文字方法
需要graph配置时定义
this.Graph = ForceGraph3D() (this.elem) // .jsonUrl('../datasets/block.json') .graphData(this.graphData) .backgroundColor('rgba(0,0,0,0)') .nodeThreeObjectExtend(true) //函数,属性或布尔值,用于在使用自定义nodeThreeObject(false)或扩展它时是否替换默认节点(true)。如果false或者不设置会使nodeThreeObject返回的模型替换原有的球体节点 .nodeThreeObject(node => { this.twoDRenderer.setSize( WIDTH, HEIGHT ); //按情况,如出现更新后文字不随节点动必要,不断更新size,防止位置会出现错乱。如果没出现可以去掉这个 return this.createAttackLabel(node); //返回需要的模型,这里是字体模型 }) ...... createAttackLabel(node){ //成功后需要在对应的位置下面去找创建的2drender的dom下面就看到了。由于可能出现被canvas覆盖的情况,所以有些时候会以为自己没有添加成功,需要通过z-index设置让文字显现出来且不影响图谱交互 let labelDiv = document.createElement('div'); labelDiv.className = 'attackLabel'; labelDiv.id = node.id; labelDiv.textContent = node.name; let label = new THREE.CSS2DObject( labelDiv ); label.position.set(0,25,0); return label; },
相比于2d字体其实用three-spritetext会更好,也更简单,有近大远小的空间感。但是精灵字体会比2d字体性能更差,可酌情选择(3d字体需要下载字体文件,占用空间和加载性能最好不要用)
import SpriteText from 'three-spritetext'; //添加文字 addSpriteText(node){ const sprite = new SpriteText(node.id); sprite.color = '#fff'; sprite.textHeight = 10; sprite.position.set(0,12,0); return sprite; }, //......使用 this.Graph = ForceGraph3D() (this.elem) .nodeThreeObject(node=>{ return this.addSpriteText(node); }) .nodeThreeObjectExtend((node=>{ return true }))
这里我用的是默认的d3引擎,如果需要实现更深入的重力引力等调整需要对d3力导图有一定的了解,对照官方文档和d3写法,就可以找到解决问题的方案。如果需要做更多的3d效果可以参考three.js,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。