相关文章推荐
爱运动的豌豆  ·  MySql5.6 ...·  1 年前    · 

近期工作中接触到vis.js,使用了他的network功能,感觉与d3有点儿像,但是操作起来要比d3方便,以下是我的使用随笔。

vis.js 下载地址( https://almende.github.io/vis ),官网上的API是纯英文的,可能对我这种英文一般的人来说使用起来就有点差强人意,无意间在别人的博文中看到一篇译文( https://blog.csdn.net/ipinki1218/article/details/83651961

本文主要讲一下vis.js如何实现右键操作,实际效果图如下所示
在这里插入图片描述
在这里插入图片描述
前期准备
我前台用的是vue + element-UI
1.下载vis.js
2.下载vue.js
3.下载elment-ui插件
4.下载lodash.min.js

HTML页面
首先新建一个topo.html页面,引入vis.js,vue.js等

<!DOCTYPE html>
<html lang="zh-CN">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../css/font-awesome.min.css">
<link rel="stylesheet" href="../element-ui/v2.10.1/theme-chalk/index.css">
<link href="../visjs/vis.min.css" rel="stylesheet" type="text/css" />
<style>
body {
	margin: 0px;
#mynetwork {
	min-height: 500px;
	width: 100%;
.btn {
	position: absolute;
	top: 10px;
	right: 10px;
.custom-menu {
  	position: absolute;
	padding:5px 0px;
    background-color: rgb(84, 92, 100);
    top:100px;
  	left:100px;
  	z-index:9999;
  	display:none;
.custom-menu div {
	padding:5px 0px;
	text-align: center;
.custom-menu div a{
	font-family:"微软雅黑";
  	font-size:14px;
  	color: rgb(255, 255, 255);
.custom-menu div a:HOVER, .custom-menu div a:ACTIVE{
	cursor: pointer; 
  	color: orange;
</style>
</head>
    <div id="app" v-cloak>
      <template>
		<div class="custom-menu" style="width:80px">
			<div><a @click="addNode">新增</a></div>
			<div><a @click="updateNode">修改</a></div>
			<div><a @click="deleteNode">删除</a></div>
    	</div>
    	<div id="mynetwork"></div>
    	<div class="btn">
    		<el-button type="success" icon="el-icon-refresh" circle @click="reloadNodeData" title="刷新"></el-button>
    	</div>
      </template>
    </div>
</body>
<script src="../jquery.min.js"></script>
<script src="../lodash.min.js"></script>
<script src="../vuejs/v2.6.10/vue.min.js"></script>
<script src="../element-ui/v2.10.1/index.js"></script>
<script src="../visjs/vis.min.js"></script>
<script src="../topo.js?_${.now?long}"></script>
</html>

JS代码
新建topo.js,代码如下

var network;
var vm = new Vue({
	el : '#app',
	data : {
		nodes : new vis.DataSet(), 
		edges : new vis.DataSet(),
		currNodeId : null  // 当前操作的节点(鼠标右键操作用)
	methods : {
		//加载网络拓扑数据,首先从缓存中获取数据,如果没有则重新加载数据
		loadNetworkData : function(){
			var _this = this;
			$.getJSON('/topo/data/load', {
				success: function(r){
					if(r && r.code == 0 && r.data && r.data.nodes && r.data.nodes.length>0 && r.data.edges && r.data.edges.length > 0){
						r.data.nodes.forEach(function(node){
							_this.nodes.add(node);
						});
						r.data.edges.forEach(function(edge){
							_this.edges.add(edge);
						});
						_this.initNetwork();
					}else{
						_this.reloadNodeData();
			});
		 * 构建数据
		buildData: function(){
			var rootId = _.uniqueId();
			var root = {
				id: rootId,
				label: '根节点',
				title: '根节点',
				group: 'root',
			_this.nodes.add(root);
			for (var i=0; i<10; i++){
				var _id = _.uniqueId();
				var name = 'node'+i;
				var node = {
					id: _id,
					label: name,
					title: name,
					group: 'group',
				_this.nodes.add(node);
				_this.edges.add({
					from : rootId,
					to : _id
				});
		//重新加载所有节点
		reloadNodeData : function(){
			this.nodes.clear();
			this.edges.clear();
			if (!!network) {
		    	network.destroy();
		        network = null;
			this.buildData();
			this.initNetwork();
		//初始化网络拓扑
		initNetwork : function() {
			var _this = this;
			var container = document.getElementById('mynetwork');
			var data = {
				'nodes' : this.nodes,
				'edges' : this.edges
			var options = {
				locale: 'cn',
				physics: {
			        stabilization: false,
			        barnesHut: {
			          //gravitationalConstant: -3000,//(默认值 : -2000)引力:值越大节点越集中,反之值越小节点越离散
			          springConstant: 0.01,//(default: 0.04)弹簧:值越大弹性越强
			          //springLength: 50//(default: 95)弹簧长度
			        minVelocity: 5 //(default: 1)一旦达到所有节点的最小速度,我们假设网络已经稳定,布局停止。
				interaction : {
					navigationButtons : true,
					keyboard : true
				// 自定义节点样式
				groups : {
					root : {
						shape : 'icon',
						icon : {
							face : 'FontAwesome',
							code : '\uf015',
							size : 50,
							color : '#0066FF'
					group : {
						shape : 'icon',
						icon : {
							face : 'FontAwesome',
							code : '\uf0c0',
							size : 50,
							color : '#00CCFF'
			network = new vis.Network(container, data, options);
			var loading;
			// 稳定启动时触发
			network.on("startStabilizing", function (params) {
				loading = _this.$loading({
			          lock: true,
			          text: '数据加载中',
			          background: 'rgba(0, 0, 0, 0.8)'
			        });
			});
			//在Network稳定或调用stopSimulation()时触发
			network.on("stabilized", function (params) {
				loading.close();
				network.fit();
				saveNetwork();
			});
			// 双击鼠标触发
			network.on("doubleClick", function (params) {
				// 双击时params.nodes.length不为0,拖动时该事件也会触发,但length=0
				if (params.nodes.length>0){
					var node = _this.nodes._data[params.nodes[0]];
					// 这里可以实现点击加载节点
			});
			// 单击鼠标触发
			network.on("click", function (params) {
				var nodeId = this.getNodeAt(params.pointer.DOM); 
				if(nodeId){
			    	var options = {
			    			scale: 1.0,
			    			offset: {x:0,y:0},
			    			animation: {
			    				duration: 1000,
			    				easingFunction: 'easeInOutQuad'
			    	network.focus(nodeId, options);		//定位交点	  		
				if (!$(".custom-menu").is(':hidden')){
					$(".custom-menu").hide();
			});
			// 单击鼠标右键触发
			network.on("oncontext", function (params) {
				var nodeId = this.getNodeAt(params.pointer.DOM); 
				if (nodeId){
					params.event.preventDefault();
				    $(".custom-menu").finish().toggle(100);
				    $(".custom-menu").css({
				        top: params.pointer.DOM.y + "px",
				        left: params.pointer.DOM.x + "px"
				    });
				    _this.currNodeId = nodeId;
			});
			var saveNetwork =  function(){
				var positions = network.getPositions();
				var data = {
					'nodes': _this.nodes.get().map(function(node){
						return _.extend({}, node, positions[node.id]);
					}),
					'edges': _this.edges.get()
				$.post('/topo/data/save', data, {
    				success: function(r){
    			});
		addNode: function(){
			// 添加节点
			var _pid = _this.currNodeId;
			var _id = _.uniqueId(node.id + "_");
			// 根据节点的children数,计算_id
			var children = _this.edges.get({
				  filter: function (item) {
					  return item.from == _pid;
				  fields:["to"]
			});
			if (children){
				_.sortBy(children, function(a, b){
					return b.repalce(/_/g, "")-a.repalce(/_/g, "");
				});
				_id = parseInt(children[0].substring(_pid.length+1))+ 1;
			var label = node.label+_id.substring(_pid.length);
			_this.nodes.add({
				id: _id,
				label: label,
				group: 'group',
				title: label,
			});
			_this.edges.add({
				from : _pid,
				to : _id
			});
		updateNode: function(){
			// 修改节点
			var node = _this.nodes.get(_this.currNodeId);
			node.title = "我正在执行修改操作";
			_this.nodes.update(node);
		deleteNode: function(){
			var _this = this;
			if (_this.currNodeId){
				var node = _this.nodes.get(_this.currNodeId);
				_this.nodes.remove(_this.currNodeId);
				// 删除所有连线
				var edgeIds = _this.edges.get({
					  filter: function (item) {
						  return item.to == _this.currNodeId;
					  fields:["id"]
				});
				_this.edges.remove(edgeIds);
				_this.currNodeId = null;
			// 删除节点之后隐藏操作菜单
			if (!$(".custom-menu").is(':hidden')){
				$(".custom-menu").hide();
	mounted : function() {
		//页面初始入口
		$('#mynetwork').height($(top).innerHeight()-100);
		if (window.ActiveXObject || "ActiveXObject" in window){
			//为IE浏览器设置宽度
			$('#mynetwork').width($(top).innerWidth());
		this.loadNetworkData();
});

JAVA代码
创建TopoController.java,将加载完成的拓扑结构保存到缓存里,再次加载时可以直接从缓存获取数据及结构(位置会和上次查询完全一样),这样做的好处有点儿类似快照,保留上次浏览结果,也可提升再次访问的加载速度。

@Controller
@RequestMapping("/topo")
public class AssetTopoController {
	@Autowired
    private RedisUtils redisUtils;
	 * 保存拓扑数据到redis中
	 * @param data
	 * @return
	@PostMapping("/data/save")
	@ResponseBody
	public void save(@RequestBody Map<String,Object> data) {
		redisUtils.set("my-network", data, 60*60); //设置过期时长为1小时
	 * 从redis中获取拓扑数据
	 * @return
	@GetMapping("/data/load")
	@ResponseBody
	public Map<String, Object> load() {
		Object data = redisUtils.get("my-network");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("data", JSONUtil.parseObj(data));
		return map;

实现右键菜单只需要两步就可以,第一找到vis.js network的右键事件,第二自定义一个菜单(或者div),在某个节点触发右键事件时定位到该节点上

第一步 找到vis.js network 的右键事件,获取当前节点的位置,用于确定右键菜单的位置
在这里插入图片描述
第二步 定义右键div 确定定位通过CSS样式来控制
在这里插入图片描述
在这里插入图片描述
这样就实现鼠标右键功能了。

另外有一点需要需注意,vis.js有自带的右键事件,使用时需要先关闭他自身的事件(关闭代码params.event.preventDefault()),然后再打开自定义菜单(显示自定义custom div),通过this.getNodeAt(params.pointer.DOM); 来获取当前选中节点。实现的过程中,我发现不管鼠标放在那里都可以调用右键事件,但是this.getNodeAt(params.pointer.DOM)只有放在节点上时才不会为空,所以可以通过this.getNodeAt(params.pointer.DOM)的返回值来判断当前鼠标的位置。我的做法是当鼠标选中某个节点时,触发鼠标右键弹出自定义菜单,离开选中节点单击鼠标关闭菜单,未选中节点时继续显示默认右键功能。
在这里插入图片描述

分享几篇别人的博文:
vis.js介绍
https://blog.csdn.net/DCX_abc/article/details/78143635

vis.js 小记
https://blog.csdn.net/qq_39759115/article/details/78594831

Vis.js–Network中文教程
https://blog.csdn.net/ipinki1218/article/details/83651961

最近工作中接触到vis.js,使用了他的network功能,感觉与D3有点儿像,但是操作起来要比D3方便,以下是我的使用随笔。前期准备1.下载vis.js (https://almende.github.io/vis)2.vue.js(我的前台使用的 Vue + ElementUI)HTML页面首先新建一个topo.html页面,引入vis.js,vue.js等&lt;!DOCTYPE html&gt;&lt;html lang="zh-CN"&gt;&lt;head&gt;&lt;met
`vis-timeline`时间轴是一个交互式可视化图表,用于实时可视化时间数据。数据项可以只与某个时间点关联,也可以有开始和结束日期(即一个时间范围)。vis-timeline可以通过拖拽和滚动时间轴自由移动和缩放。可以在时间轴中创建、编辑和删除数据项目。轴上的时间尺度是自动调整的,支持从毫秒到年的尺度。 vis-time时间轴使用常规`HTML DOM`呈现时间轴和放在时间轴上的项目,这样的好处就是可以使用自定义css样式进行灵活定制。
Vis.js是一款基于JavaScript的可视化图表库,vis.js不像其他的图表库那样仅仅支持几种常用的数据图表,比如线形图、柱状图、饼图等,Vis.js支持上百种不同类型的可视化图表类型,比如时间线图、网络图、2D图表、3D图表,每一种类型的图表下又包含几十种不同展现方式的数据图表。 更为让人震撼的是,Vis.js图表都是动态的,也就是说你可以拖动每一个数据节点来让图表数据进行动态排列。
请务必访问以获取更多信息。 渲染的图形是可滚动、可缩放、视网膜就绪、动态的,并在双击时切换布局。 由于 vis.js 的命令性质,更新图形属性会导致图形的完全重绘并将其完全移植到 React 本身就是一个大项目! 该组件将三个 vis.js 配置对象作为属性: 图:包含两个数组{边,节点} 选项:描述的普通 vis.js 选项 事件:一个对象,其为键,而其回调为值 // with npm $ npm install vis-react --save // with yarn $ yarn add vis-react 要使用组件,请在根 html 中包含 visjavascriptcss 文件: <!DOCTYPE html >
Vis.js是一个动态的、基于浏览器的可视化库,可处理大量的动态数据并能与这些数据进行交互操作。该项目包含 DataSet、Timeline, 和 Graph(2d和3d)。 Vis.js是由Almende B.V公司开发的开源项目,基于Canvas绘制web可视化图形图表,简单易用,功能强大。 下面是vis.js的模块: 数据集(DataSet): 灵活的键/值对,可添加、更
首先说明一下,为什么要学习这个东西,,,,因为我们项目中要做一个网络拓扑图,去年网上找了找用jtopo做的,然后大家说太丑了,操作也不好,反正言外之意就是要换个插件写,,,,然后我前两周就研究了一下echarts的树图和关系图graph,研究的结果我博客也有写,总的来说echarts蛮漂亮的,但是偏展示,后期如果要加很多扩展内容的话,不建议用。如果有需要可以去看echarts例子的可以去看看https://blog.csdn.net/qq_36509946/category_10330384.html
Vis.js的使用 vis.js 基于浏览器的动态可视化库。该库被设计为易于使用,处理大量的动态数据,并支持对数据的操作和交互。该库由组件DataSet,Timeline,Network,Graph2d和Graph3d组成。这里主要用到Network(网状图) network实例请参考官网:http://visjs.org/network_examples.html。 network是一种可...
&amp;amp;amp;lt;!DOCTYPE html&amp;amp;amp;gt; &amp;amp;amp;lt;html lang=&amp;amp;amp;quot;en&amp;amp;amp;quot;&amp;amp;amp;gt; &amp;amp;amp;lt;head&amp;amp;amp;gt; &amp;amp;amp;lt;meta charset=&amp;amp;amp;quot;U
Vis.js 是一个动态的基于浏览器的可视化库,特点是易用,可处理大量的动态数据,并与这些数据进行交互操作。该库包含 DataSet, Timeline, and Graph.示例代码:   <title>Timeline | Basic demo</title>   [removed][removed]   <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" />   <style type="text/css">     body, html {       font-family: sans-serif;   </style> </head> [removed]   var container = document.getElementById('mytimeline');   var data = [     {id: 1, content: 'item 1', start: '2013-04-20'},     {id: 2, content: 'item 2', start: '2013-04-14'},     {id: 3, content: 'item 3', start: '2013-04-18'},     {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},     {id: 5, content: 'item 5', start: '2013-04-25'},     {id: 6, content: 'item 6', start: '2013-04-27'}   var options = {};   var timeline = new vis.Timeline(container, data, options); [removed] </body> </html>可视化效果: 标签:Visjs
TiddlyWiki 由Jeremy Ruston创建(Jeremy [at] jermolene [dot] com) 根据以下发布: BSD 3条款“新”或“经修订”许可证(包括在许可的情况下采用任何将来版本的许可证的权利) 知识共享署名3.0(包括在许可的情况下采用许可的任何将来版本的任何权利) vis.js库 根据以下许可发布: Apache许可证2.0版,2004年1月 MIT许可证(MIT)
R 包,使用 vis.js 库进行 3D 可视化 devtools::install_github("DataKnowledge/vis3D") require(vis3D) ?vis3D nb <- 10 don <- expand.grid(x = 1:nb, y = 1:nb) don$z <-rnorm(nb*nb) vis3D(don) shiny::runApp(system.file("shiny", package = "vis3D"))
/usr/bin/ld: ../../Lib/libVis.a(Vis.cpp.o): in function `Vis3d_Command_CreateView(Vis3d*, Command*)': Vis.cpp:(.text+0xd0fe): undefined reference to `osg::GraphicsContext::getWindowingSystemInterface()'
这是一个链接错误,指向 `osg::GraphicsContext::getWindowingSystemInterface()` 未被定义。这通常是由于链接器无法找到必要的库或链接到错误的库引起的。 解决方法: 1. 确保你已经正确地链接了 OpenSceneGraph 库。 2. 确认你使用的是正确版本的 OpenSceneGraph 库。可能会有多个版本的库存在,而你使用的链接库可能不是你想要的那个版本。如果你使用的是旧版本的库,可能会缺少你需要的某些符号。 3. 如果你已经正确地链接了 OpenSceneGraph 库,但是仍然遇到此错误,请尝试重新编译 OpenSceneGraph 库并重新链接你的应用程序。在重新编译时确保在编译选项中包含了 `getWindowingSystemInterface` 的实现。 希望这些解决方法能够帮助你解决问题。