用TWaver HTML5定制五彩斑斓的链路
最近有客户提到自定义链路的需求,个人感觉非常有代表意义,现在共享出来给大家参考一下。先来看看需求:
合并前:
合并后:
进入子网后,节点上显示和上层节点的连线信息进入子网前,节点2和子网内节点4之间有链路:进入子网后,节点4上也显示此链路:
先看看实现的效果,后面我们慢慢解释如何定制链路:
前5个需求可以通过自定义Link和LinkUI实现,需要注意:
完整代码如下:
// 自定义Node构造函数demo.ScaleNode = function(id) { // 调用基类构造函数demo.ScaleNode.superClass.constructor.call(this, id);};// 设置自定义Node继承twaver.Nodetwaver.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.NodeUItwaver.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<count; i++) { link = links[i]; offset = link.getStyle('link.bundle.offset'); shortenLength = link.getClient('shortenLength'); textWidth = ctx.measureText(link.getName()).width; if (position === 'left') { terminal = {x: center.x - offset - shortenLength, y: center.y + gap}; tempCenter = {x: center.x - offset, y: center.y + gap}; textCenter = {x: terminal.x - textWidth/2 - 10, y: terminal.y}; angle = Math.PI/2; } else if (position === 'right') { terminal = {x: center.x + offset + shortenLength, y: center.y + gap}; tempCenter = {x: center.x + offset, y: center.y + gap}; textCenter = {x: terminal.x + textWidth/2 + 10, y: terminal.y}; angle = Math.PI/2; } else if (position === 'top') { terminal = {x: center.x + gap, y: center.y - offset - shortenLength}; tempCenter = {x: center.x + gap, y: center.y - offset}; textCenter = {x: terminal.x, y: terminal.y - 10}; angle = 0; } else { terminal = {x: center.x + gap, y: center.y + offset + shortenLength}; tempCenter = {x: center.x + gap, y: center.y + offset}; textCenter = {x: terminal.x, y: terminal.y + 10}; angle = 0; } gap += 20; var isFrom = link.getFromNode() === this.getElement(), points; if (isFrom) { points = new twaver.List([tempCenter, terminal]); } else { points = new twaver.List([terminal, tempCenter]); } network.getElementUI(link)._paintBody(ctx, points, angle);ctx.textAlign = 'center';ctx.textBaseline = 'middle';ctx.fillStyle = 'black';// 另一端节点标签var name = isFrom ? link.getToNode().getName() : link.getFromNode().getName();ctx.fillText(name, textCenter.x, textCenter.y);textCenter = {x: (tempCenter.x + terminal.x)/2, y: (tempCenter.y + terminal.y)/2};// Link标签ctx.fillText(link.getName(), textCenter.x, textCenter.y);// 画起始箭头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'); } } }, // 获取不同方位的上层链路集合getAttachedLinks: function () {var currentSubNetwork = this.getNetwork().getCurrentSubNetwork();if (!currentSubNetwork || !this.getElement().getLinks()) {return null;}var result;this.getElement().getLinks().forEach(function (link) {var fromSubNetwork = twaver.Util.getSubNetwork(link.getFromNode()),toSubNetwork = twaver.Util.getSubNetwork(link.getToNode());if (fromSubNetwork !== toSubNetwork) {if (!result) {result = {};}var fromCenter = link.getFromNode().getCenterLocation(),toCenter = link.getToNode().getCenterLocation(),angle = getAngle(fromCenter, toCenter),isOut = currentSubNetwork === fromSubNetwork,position;if (isOut) {if (fromCenter.x <= toCenter.x) {if (angle >= -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;}});
本文完整代码见附件:见原文最下方