近期工作中接触到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;
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: {
springConstant: 0.01,
minVelocity: 5
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.on("stabilized", function (params) {
loading.close();
network.fit();
saveNetwork();
});
network.on("doubleClick", function (params) {
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 + "_");
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){
$('#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);
* 从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等<!DOCTYPE html><html lang="zh-CN"><head><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 中包含 vis 的 javascript 和 css 文件:
<!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;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;amp;quot;en&amp;amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
&amp;amp;lt;meta charset=&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` 的实现。
希望这些解决方法能够帮助你解决问题。