合并前:
合并后:
进入子网后,节点4上也显示此链路:
先看看实现的效果,后面我们慢慢解释如何定制链路:
前5个需求可以通过自定义Link和LinkUI实现,需要注意:
完整代码如下:
// 自定义Link构造函数
demo.ScaleLink = function(id, from, to) {
// 调用基类构造函数
demo.ScaleLink.superClass.constructor.call(this, id, from, to);
// 设置链路宽度为10个像素
this.setStyle('link.width', 10);
//this.setStyle('link.color', 'rgba(0, 0, 0, 0)');
// 设置Link类型为平行
this.setStyle('link.type', 'parallel');
// 设置链路捆绑的间距为40
this.setStyle('link.bundle.offset', 40);
// 设置刻度颜色
this.setClient('scaleColor', 'black');
// 设置刻度宽度
this.setClient('scaleWidth', 1);
// 设置刻度个数
this.setClient('scaleNumbers', 4);
// 设置是否变短
this.setClient('shortened', false);
// 设置变短后的长度
this.setClient('shortenLength', 100);
// 设置分割线颜色
this.setClient('splitterColor', 'black');
// 设置起始填充百分比
this.setClient('fromFillPercent', 0);
// 设置结束填充百分比
this.setClient('toFillPercent', 0);
};
// 设置自定义Link继承twaver.Link
twaver.Util.ext('demo.ScaleLink', twaver.Link, {
// 重载获取UI类方法,返回自定义UI类
getCanvasUIClass : function () {
return demo.ScaleLinkUI;
},
// 根据百分比获取填充颜色
getFillColor: function(percent) {
if (percent < 0.25) {
return 'green';
}
if (percent < 0.5) {
return 'yellow';
}
if (percent < 0.75) {
return 'magenta';
}
return 'red';
},
// 获取起始填充颜色
getFromFillColor: function () {
return this.getFillColor(this.getFromFillPercent());
},
// 获取结束填充颜色
getToFillColor: function () {
return this.getFillColor(this.getToFillPercent());
},
// 获取起始百分比
getFromFillPercent: function () {
// 如果是链路捆绑代理,返回所有捆绑链路中填充百分比最大的值
if (this.isBundleAgent()) {
var fromAgent = this.getFromAgent(),
percentKey, maxPercent = 0, percent;
this.getBundleLinks().forEachSiblingLink(function (link) {
percentKey = fromAgent === link.getFromAgent() ? 'fromFillPercent' : 'toFillPercent';
percent = link.getClient(percentKey);
maxPercent = percent > maxPercent ? percent : maxPercent;
});
return maxPercent;
} else {
return this.getClient('fromFillPercent');
}
},
// 获取结束百分比
getToFillPercent: function () {
// 如果是链路捆绑代理,返回所有捆绑链路中填充百分比最大的值
if (this.isBundleAgent()) {
var toAgent = this.getToAgent(),
percentKey, maxPercent = 0, percent;
this.getBundleLinks().forEachSiblingLink(function (link) {
percentKey = toAgent === link.getToAgent() ? 'toFillPercent' : 'fromFillPercent';
percent = link.getClient(percentKey);
maxPercent = percent > maxPercent ? percent : maxPercent;
});
return maxPercent;
} else {
return this.getClient('toFillPercent');
}
},
// 重载获取网元名称方法,判断如果是链路捆绑代理,就返回起始和结束代理节点的名称
getName: function () {
if (this.getClient('shortened')) {
return null;
} else if (this.isBundleAgent()) {
return this.getFromAgent().getName() + '-' + this.getToAgent().getName();
} else {
return demo.ScaleLink.superClass.getName.call(this);
}
}
});
// 自定义LinkUI构造函数
demo.ScaleLinkUI = function(network, element){
// 调用基类构造函数
demo.ScaleLinkUI.superClass.constructor.call(this, network, element);
};
// 设置自定义Link继承twaver.canvas.LinkUI
twaver.Util.ext('demo.ScaleLinkUI', twaver.canvas.LinkUI, {
// 获取Link角度
getAngle: function () {
return getAngle(this.getFromPoint(), this.getToPoint());
},
// 获取Link中间点
getMiddlePoint: function (from, to, percent) {
return {
x: from.x + (to.x - from.x) * percent,
y: from.y + (to.y - from.y) * percent
};
},
// 画刻度线
drawScaleLine: function (from, to, angle, length, ctx, percent, lineWidth, lineColor) {
var point = this.getMiddlePoint(from, to, percent);
var y = length/2 * Math.sin(angle),
x = length/2 * Math.cos(angle);
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.moveTo(point.x + x, point.y + y);
ctx.lineTo(point.x - x, point.y -y);
ctx.stroke();
},
// 获取是否将链路变短
isShorten: function () {
var link = this.getElement();
return link.getClient('shortened') && this.getLineLength() > link.getClient('shortenLength') * 2;
},
// 重载画链路函数,用自定义逻辑画链路
paintBody: function (ctx) {
var points = this.getLinkPoints(),
link = this.getElement();
if (!points || points.size() < 2) {
return;
}
var lineLength = this.getLineLength(),
shortenLength = link.getClient('shortenLength'),
percent = shortenLength / lineLength,
from = points.get(0),
to = points.get(1),
angle = this.getAngle() + Math.PI/2;
if (this.isShorten()) {
fromPoints = new twaver.List([from, this.getMiddlePoint(from, to, percent)]);
toPoints = new twaver.List([this.getMiddlePoint(from, to, 1 - percent), to]);
this._paintBody(ctx, fromPoints, angle);
this._paintBody(ctx, toPoints, angle);
// 画文字
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = 'black';
var textCenter = {x: (fromPoints.get(0).x + fromPoints.get(1).x)/2, y: (fromPoints.get(0).y + fromPoints.get(1).y)/2};
ctx.fillText(link.getName(), textCenter.x, textCenter.y);
textCenter = {x: (toPoints.get(0).x + toPoints.get(1).x)/2, y: (toPoints.get(0).y + toPoints.get(1).y)/2};
ctx.fillText(link.getName(), textCenter.x, textCenter.y);
ctx.fillText(link.getToNode().getName(), fromPoints.get(1).x, fromPoints.get(1).y);
ctx.fillText(link.getFromNode().getName(), toPoints.get(0).x, toPoints.get(0).y);
} else {
this._paintBody(ctx, points, angle);
}
// 画起始箭头
if (link.getClient('arrow.from')) {
twaver.Util.drawArrow(ctx, 12, 9, points, true, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
}
// 画结束箭头
if (link.getClient('arrow.to')) {
twaver.Util.drawArrow(ctx, 12, 9, points, false, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
}
},
_paintBody: function (ctx, points, angle) {
var link = this.getElement(),
width = link.getStyle('link.width'),
grow = width,
outerColor = this.getOuterColor();
if (outerColor) {
var outerWidth = link.getStyle('outer.width');
grow += outerWidth * 2;
}
var selectBorder = !this.getEditAttachment() && link.getStyle('select.style') === 'border' && this.getNetwork().isSelected(link);
if (selectBorder) {
var selectWidth = link.getStyle('select.width');
grow += selectWidth * 2;
}
ctx.lineCap = link.getStyle('link.cap');
ctx.lineJoin = link.getStyle('link.join');
// 画选中边框
if (selectBorder) {
this.drawLinePoints(ctx, points, grow, link.getStyle('select.color'));
}
// 画边框
if (outerColor) {
this.drawLinePoints(ctx, points, width + outerWidth * 2, outerColor);
}
// 画Link
this.drawLinePoints(ctx, points, width, this.getInnerColor() || link.getStyle('link.color'));
var fromFillPercent = link.getFromFillPercent(),
toFillPercent = link.getToFillPercent(),
fromFillColor = link.getFromFillColor(),
toFillColor = link.getToFillColor(),
from = points.get(0),
to = points.get(1);
var x = from.x + (to.x - from.x) / 2 * fromFillPercent,
y = from.y + (to.y - from.y) / 2 * fromFillPercent;
var middle = {x: x, y: y};
var fromPoints = new twaver.List([from, middle]);
// 画起始填充色
this.drawLinePoints(ctx, fromPoints, width, fromFillColor);
from = points.get(1);
to = points.get(0);
x = from.x + (to.x - from.x) / 2 * toFillPercent;
y = from.y + (to.y - from.y) / 2 * toFillPercent;
middle = {x: x, y: y};
var toPoints = new twaver.List([from, middle]);
// 画结束填充色
this.drawLinePoints(ctx, toPoints, width, toFillColor);
from = points.get(0);
to = points.get(1);
var scaleWidth = link.getClient('scaleWidth'),
scaleColor = link.getClient('scaleColor');
// 画刻度
for (var i = 1, n = link.getClient('scaleNumbers') * 2; i < n; i++) {
this.drawScaleLine(from, to, angle, width/2, ctx, i/n, scaleWidth, scaleColor);
}
// 画分隔线
this.drawScaleLine(from, to, angle, width, ctx, 0.5, 3, link.getClient('splitterColor'));
}
});
最后一个需求可以通过定制Node和NodeUI达到目的:
// 自定义Node构造函数
demo.ScaleNode = function(id) {
// 调用基类构造函数
demo.ScaleNode.superClass.constructor.call(this, id);
};
// 设置自定义Node继承twaver.Node
twaver.Util.ext('demo.ScaleNode', twaver.Node, {
getCanvasUIClass: function () {
return demo.ScaleNodeUI;
}
});
// 自定义NodeUI构造函数
demo.ScaleNodeUI = function(network, element){
// 调用基类构造函数
demo.ScaleNodeUI.superClass.constructor.call(this, network, element);
};
// 设置自定义NodeUI继承twaver.canvas.NodeUI
twaver.Util.ext('demo.ScaleNodeUI', twaver.canvas.NodeUI, {
// 重载画网元方法,画上层链路
paintBody: function (ctx) {
demo.ScaleNodeUI.superClass.paintBody.call(this, ctx);
var result = this.getAttachedLinks();
if (!result) {
return;
}
for (var position in result) {
this.paintLink(ctx, result[position], position);
}
},
// 画链路
paintLink: function (ctx, links, position) {
var center = this.getElement().getCenterLocation(),
count = links.length,
half = count / 2,
network = this.getNetwork(),
gap = (count - 1) * -10,
terminal, link, i, offset, shortenLength, angle, tempCenter, textWidth, textHeight = 20, textCenter;
for (i=0; i= -Math.PI/4 && angle <= Math.PI/4) {
position = 'right';
} else if (angle > Math.PI/4) {
position = 'bottom';
} else {
position = 'top';
}
} else {
if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
position = 'left';
} else if (angle > Math.PI/4) {
position = 'top';
} else {
position = 'bottom';
}
}
} else {
if (fromCenter.x <= toCenter.x) {
if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
position = 'left';
} else if (angle > Math.PI/4) {
position = 'top';
} else {
position = 'bottom';
}
} else {
if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
position = 'right';
} else if (angle > Math.PI/4) {
position = 'bottom';
} else {
position = 'top';
}
}
}
if (!result[position]) {
result[position] = [];
}
result[position].push(link);
}
});
return result;
}
});
本文完整代码见附件:见原文最下方