首先推荐工具库JTopo: jtopo 一个好用的交互式HTML5图形库
其他的看官方文档
三、基础讲解
jtopo的核心对象有Stage、Layer、Canvas、Node、Link, 关系如下图:
![](http://www.jtopo.com/images/md/jtopo_sys.png)
Stage、Layer和Node关系
顶层对象(Stage)
jtopo的核心对象之间存在层级结构,最顶层的为Stage,管理一个或者多个Layer,可以对Layer进行管理:添加、移除。
提供一些常规性的交互功能,比如:鼠标缩放、视图模式变换(普通、框选、拖拽、编辑、锁定);
显示控制:按画布居中、1:1显示 导出图片等功能。
代码示例:
var stage = new Stage(“divId”);
stage.show(); // 显示出来
详细的:Stage-API参考
层对象(Layer)
Layer 是一个抽象对象,默认是完全透明的,上层对象为Stage.
一个Layer下面可以有多个Node、Link对象,Node、Link对象只有放入Layer后才可能被绘制出来。
Layer 可以被平移、缩放,用户可以通过鼠标在画布上的拖拽 和 鼠标滚轮完成,也可以通过API来修改Layer的x、y坐标和缩放系数scaleX和scaleY实现同等效果。
一个Layer对象对应一个Canvas,多个Layer常用于画面逻辑分层,比如有的层绘制速度较慢,有的层绘制速度较快,有的层作为背景层,有的作为动画层。
代码示例:
var stage = new Stage();
var layer = new Layer(“layer-1”);
stage.addChild(layer); // 放入Stage
//…
stage.show(); // 显示出来
详细的:Layer-API参考
节点对象(Node)
用户操作的核心两个对象Node 、Link之一。
Node给人的形象是一个矩形,有坐标(x,y) 和 宽高尺寸(width、heigh)。
可以指定一个文本字符串,默认显示在中矩形的下面。
Node对象的外观可以通过css方法设置,核心的外观属性有:边框颜色、填充颜色、字体颜色、字体(大小、加粗等CSS Font支持的都可以)、圆角。
代码示例:
var stage = new Stage();
var layer = new Layer(“layer-1”);
stage.addChild(layer); // 放入Stage
var node = new Node(“Node-1”);
layer.addChild(node); // 放入Layer
stage.show(); // 显示出来
各种Node效果如下:
![](http://www.jtopo.com/images/md/nodes.png)
参考:Node演示
详细的:Node-API参考
连线对象(Link)
用户操作的核心两个对象之一。
Link给人的形象是连线,有起始点和结束点,一般用来表示关系、流向等。
可以指定一个文本字符串,默认显示在连线的中间。
Link对象的外观可以通过css方法设置,核心的外观属性有:颜色、线条粗细、字体颜色、字体(大小、加粗等CSS Font支持的都可以)。
代码示例:
var link = new Link('Link',fromNode,toNode);
layer.addChild(link);
各种Link效果如下:
![](http://www.jtopo.com/images/md/links.png)
参考:Link演示 详细的:Link-API参考
外观样式(Style)
样式大部分属性命名和效果都遵循和参考了Html5-css。
var stage = new Stage('divId');
var layer = new Layer('default');
stage.addChild(layer);
// 样式大部分属性命名和效果都遵循Html5-Canvas的绘图定义
// 其本质是:绘制每个图元前 去修改Canvas 绘图环境context的属性
// 父节点只有某些属性可以影响到子节点(例如全局性的阴影、字体、
// 某些子节点的样式为空的时候父样式才可能生效)
// 全局字体
layer.css({
// 字体,格式CSS-FONT
'font': 'bold 12px 仿宋',
});
let circleNode = new CircleNode('节点', 60, 300, 100);
// 可以用类似css语法设置更多属性, 涉及语法详细可以参考CSS相关教程。
circleNode.css({
width: 48,
height: '48px',
border: 'solid 1px gray',
borderRadius: 5,
background: "white url('./demo/img/node2.png') no-repeat",
backgroundSize: '32px 32px',
backgroundPosition: 'center center',
zIndex: 2,
font: 'bold 11px arial',
color: 'gray',
textPosition: 'center', // 位置
textAlign: 'center', // 左右居中
textBaseline: 'middle' // 上下居中
});
layer.addChild(circleNode);
stage.show();
事件处理(鼠标交互)
jtopo封装了鼠标行为,可以在Node或者Link对象上增加事件监听,代码示例:
var stage = new Stage('divId');
var layer = new Layer('default');
stage.addChild(layer);
var node = new Node('From', 200, 150, 40, 40);
node.setImage('./demo/img/laptop.png', true);
layer.addChild(node);
stage.show();
// 鼠标点击
node.on('click', function (event) {
// 更详细的事件信息,
// 比如:鼠标在画布上的x,y坐标, 鼠标状态(是否拖拽开始、结束), 相对于上一次鼠标位置的偏移量(dx,dy)等
let eventDetails = event.details;
node.text = 'click';
console.log('click');
});
// 鼠标双击
node.on('dblclick', function (event) {
console.log('dblclick');
node.text = 'dblclick';
});
// 鼠标进入
node.on('mouseenter', function (event) {
console.log('mouseenter');
node.text = 'mouseenter';
});
// 鼠标移动
node.on('mousemove', function (event) {
console.log('mousemove');
node.text = 'mousemove';
});
// 鼠标离开
node.on('mouseout', function (event) {
node.text = 'mouseout';
console.log('mouseout');
});
// 鼠标按下
node.on('mousedown', function (event) {
console.log('mousedown');
node.text = 'mousedown';
});
// 鼠标松开
node.on('mouseup', function (event) {
console.log('mouseup');
node.text = 'mouseup';
});
// 鼠标拖拽
node.on('mousedrag', function (event) {
console.log('mousedrag');
node.text = 'mousedrag';
});
// 鼠标拖拽结束
node.on('mousedragend', function (event) {
console.log('mousedragend');
node.text = 'mousedragend';
});
node.on('touchstart', function (event) {
node.text = 'touchstart';
});
node.on('touchmove', function (event) {
node.text = 'touchmove';
});
node.on('touchend', function (event) {
node.text = 'touchend';
});
// --- 下面是较为高级的事件处理示例
// 阻止默认拖拽处理, 参考:Html事件的preventDefault() 说明
function preventDefaultDrag(e) {
e.preventDefault();
}
function disabledDefaultMouseDragHandler(obj) {
obj.on('mousedrag', preventDefaultDrag);
}
function enabledDefaultMouseDragHandler(obj) {
obj.removeEventListener('mousedrag', preventDefaultDrag);
}
// 阻止默认拖拽处理
node.on('mousedrag', preventDefaultDrag);
// 定义自己的拖拽处理
function customDragHandler(event) {
// 每次拖拽偏移量
console.log(event.dx + ' - ' + event.dy);
//....
// 还可以再次抛出自己的事件
node.dispatchEvent(new Event('myEvent'));
}
// 使用自己的事件处理 (on 等价 addEventListener,简写而已)
node.on('mousedrag', customDragHandler);
// 捕获自己抛出的自定义事件
node.on('myEvent', function (event) {
console.log('我的事件');
console.log(event);
});
// 恢复默认拖拽
node.removeEventListener('mousedrag', preventDefaultDrag);
// 删除自己的拖拽处理
node.removeEventListener('mousedrag', customDragHandler);
参考:事件处理演示
父子关系
Node和Link都有parent 和 children 属性
//...
layer.removeAllChild(); // 清空
layer.children.length == 0; // true
let node = new Node();
layer.addChild(node); // node.parent === layer -> true
layer.chidlren[0] === node; // true node 在 layer.chidlren集合里
//...
layer.addChild(node2);
layer.addChild(link3);
// 此时node、node2 和 link3 的 parent相同
zIndex
zIndex用来控制前后遮挡关系(同父类下的直接子节点之间有效).
数值越大越靠前面,越小越靠后,前面会遮挡后面。
Node 和 Link 默认的zIndex值分别为 2 和 1, 所以默认同层的Node遮盖Link。
// 示例 :
fromNode.zIndex = 3;
toNode.zIndex = 4;
link.zIndex = toNode.zIndex + 1;
动画
var stage = new Stage('divId');
var layer = new Layer();
stage.addChild(layer);
var node = new Node('动画演示', stage.width / 2 - 100, stage.height / 2 - 100, 100, 100);
node.css({
'borderColor': 'black',
'textPosition': 'center',
});
layer.addChild(node);
// 在6000毫秒内,n从0逐渐变为2*Math.PI
let animation = new Animation(0, 2 * Math.PI, 6000, function (n) {
// 旋转
node.rotateTo(n);
// 重绘
layer.update();
});
// 播放
animation.play().then(() => {
node.text = '正常结束';
}).catch(() => {
node.text = '终止或结束';
});
// 暂停
node.on('mouseenter', () => {
animation.pause();
});
// 继续
node.on('mouseout', () => {
animation.continue();
});
// 停止
node.on('click', () => {
animation.stop();
});
// 播放
node.on('dblclick', () => {
animation.play();
});
stage.show();
参考:动画演示 详细的:Animation-API参考
四、布局
布局功能允许用户把Node和Link按照一定形式调整相对坐标
网格布局(GridLayout)
网格布局是较为简单的一种布局,把Node对象按照行、列的方式摆放
// 存放要布局的节点数组
var nodes = [];
// 生成9个节点
for(var i=0; i<9; i++){
var node = new Node(''+i, 0,0,32,32);
nodes.push(node);
}
// 网格式布局
var layout = new GridLayout(rows, cols);
// 节点间隔
layout.setMargin(40, 40, 0, 0);
// 动画时间, 毫秒, 不设置,就没有动画效果.
layout.setTime(1500);
// 布局的每一步回调(这里是刷新画面)
layout.onLayout(() => layer.update());
// 布局后的中心
// 布局后的中心点
layout.setCenter(stage.width * 0.5, stage.height * 0.5);
// 执行布局
gridLayout.doLayout(nodes).then(=> {
// 布局执行结束
});
效果:
![](http://www.jtopo.com/images/md/grid_layout.png)
参考:网格布局演示
详细的:GridLayout-API参考
树形布局(TreeLayout)
树形布局根据Node和Link的关系自动找到根节点,然后递归式的逐级布局为一棵树的形状。 可以指定树的朝向。
let objects = []; // Node和Link组成的集合
// 方向: up、down、left、right
// 左右间隔量 40, 上下间隔量 80
var layout = new TreeLayout('down');
layout.setMargin(0, 50, 80);
// 动画时间, 毫秒, 不设置,就没有动画效果.
layout.setTime(1200);
// 布局的每一步回调(这里是刷新画面)
layout.onLayout(() => layer.update());
// 布局后的中心
layout.setCenter(stage.width * 0.5, stage.height * 0.5);
// 生成虚拟树,取第一棵
let vTrees = new Graph(objects).toTrees();
let vTree = vTrees[0];
// 执行布局
layout.doLayout(vTree).then(() => {
// 结束
});
效果:
![](http://www.jtopo.com/images/md/tree_layout.png)
参考:树形布局演示
详细的:Layer-API参考
流式布局(FlowLayout)
圆形布局将多个节点以圆形进行布局。
var stage = new Stage('divId');
var layer = new Layer();
stage.addChild(layer);
layer.css({
'shadowColor': '#E0E0E0',
'shadowBlur': 7,
'shadowOffsetX': 2,
'shadowOffsetY': 2,
'font': 'bold 10px arial'
});
const nodes = [];
// 随机生成节点
for (var i = 0; i < 20; i++) {
var node = new Node('' + i);
node.resizeTo(32 + (50 * Math.random()),
32 + (50 * Math.random()));
node.textOffsetY = 2;
node.x = stage.width * Math.random();
node.y = stage.height * Math.random();
node.css({
'borderColor': null, // 无边框
'backgroundColor': randomColor()
});
nodes.push(node);
layer.addChild(node);
}
stage.show();
// 流式布局
var layout = new FlowLayout();
layout.setSize(500);
// 动画时间, 毫秒, 不设置,就没有动画效果.
layout.setTime(1000);
// 节点间隔
layout.setMargin(0, 3);
// 布局的每一步回调(这里是刷新画面)
layout.onLayout(() => layer.update());
// 布局后的中心点
layout.setCenter(stage.width * 0.5, stage.height * 0.5);
// 布局, 结束后调整尺寸再次布局, 反复几次
layout.doLayout(nodes);
效果:
标签:node,Node,layer,自定义,js,var,new,网络拓扑,stage From: https://www.cnblogs.com/zifayin/p/17075092.html