TWaver - 專注UI技術

          http://twaver.servasoft.com/
          posts - 171, comments - 191, trackbacks - 0, articles - 2
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          用TWaver HTML5定制五彩斑斕的鏈路

          Posted on 2012-07-17 11:20 TWaver 閱讀(1566) 評論(0)  編輯  收藏
          最近有客戶提到自定義鏈路的需求,個人感覺非常有代表意義,現在共享出來給大家參考一下。先來看看需求:
          1. 鏈路要分成兩半,用兩種顏色填充。
          2. 填充百分比在不同值域時,用不同顏色。
          3. 顯示刻度
          4. 有個開關,可以控制鏈路變短,變短后,鏈路只畫開始和結束部分(相當于原始鏈路的縮影),中間不畫
          5. 如果有多條鏈路,鏈路合并后兩端分別顯示這些鏈路中的最高填充百分比
          6. 合并前:

            合并后:


          7. 進入子網后,節點上顯示和上層節點的連線信息
          8. 進入子網前,節點2和子網內節點4之間有鏈路:

            進入子網后,節點4上也顯示此鏈路:

            先看看實現的效果,后面我們慢慢解釋如何定制鏈路:

          前5個需求可以通過自定義Link和LinkUI實現,需要注意:
          1. 用Link#getBundleLinks獲取所有的捆綁鏈路
          2. 用LinkUI#drawLinePoints畫線
          完整代碼如下:

            1 // 自定義Link構造函數
            2 demo.ScaleLink = function(id, from, to) {
            3     // 調用基類構造函數
            4     demo.ScaleLink.superClass.constructor.call(this, id, from, to);
            5     // 設置鏈路寬度為10個像素
            6     this.setStyle('link.width', 10);
            7     //this.setStyle('link.color', 'rgba(0, 0, 0, 0)');
            8     // 設置Link類型為平行
            9     this.setStyle('link.type', 'parallel');
           10     // 設置鏈路捆綁的間距為40
           11     this.setStyle('link.bundle.offset', 40);
           12     // 設置刻度顏色
           13     this.setClient('scaleColor', 'black');
           14     // 設置刻度寬度
           15     this.setClient('scaleWidth', 1);
           16     // 設置刻度個數
           17     this.setClient('scaleNumbers', 4);
           18     // 設置是否變短
           19     this.setClient('shortened', false);
           20     // 設置變短后的長度
           21     this.setClient('shortenLength', 100);
           22     // 設置分割線顏色
           23     this.setClient('splitterColor', 'black');
           24     // 設置起始填充百分比
           25     this.setClient('fromFillPercent', 0);
           26     // 設置結束填充百分比
           27     this.setClient('toFillPercent', 0);
           28 };
           29 // 設置自定義Link繼承twaver.Link
           30 twaver.Util.ext('demo.ScaleLink', twaver.Link, {
           31     // 重載獲取UI類方法,返回自定義UI類
           32     getCanvasUIClass : function () {
           33         return demo.ScaleLinkUI;
           34     },
           35     // 根據百分比獲取填充顏色
           36     getFillColor: function(percent) {
           37         if (percent < 0.25) {
           38             return 'green';
           39         }
           40         if (percent < 0.5) {
           41             return 'yellow';
           42         }
           43         if (percent < 0.75) {
           44             return 'magenta';
           45         }
           46         return 'red';
           47     },
           48     // 獲取起始填充顏色
           49     getFromFillColor: function () {
           50         return this.getFillColor(this.getFromFillPercent());
           51     },
           52     // 獲取結束填充顏色
           53     getToFillColor: function () {
           54         return this.getFillColor(this.getToFillPercent());
           55     },
           56     // 獲取起始百分比
           57     getFromFillPercent: function () {
           58         // 如果是鏈路捆綁代理,返回所有捆綁鏈路中填充百分比最大的值
           59         if (this.isBundleAgent()) {
           60             var fromAgent = this.getFromAgent(),
           61                 percentKey, maxPercent = 0, percent;
           62             this.getBundleLinks().forEachSiblingLink(function (link) {
           63                 percentKey = fromAgent === link.getFromAgent() ? 'fromFillPercent' : 'toFillPercent';
           64                 percent = link.getClient(percentKey);
           65                 maxPercent = percent > maxPercent ? percent : maxPercent;
           66             });
           67             return maxPercent;
           68         } else {
           69             return this.getClient('fromFillPercent');
           70         }
           71     },
           72     // 獲取結束百分比
           73     getToFillPercent: function () {
           74         // 如果是鏈路捆綁代理,返回所有捆綁鏈路中填充百分比最大的值
           75         if (this.isBundleAgent()) {
           76             var toAgent = this.getToAgent(),
           77                 percentKey, maxPercent = 0, percent;
           78             this.getBundleLinks().forEachSiblingLink(function (link) {
           79                 percentKey = toAgent === link.getToAgent() ? 'toFillPercent' : 'fromFillPercent';
           80                 percent = link.getClient(percentKey);
           81                 maxPercent = percent > maxPercent ? percent : maxPercent;
           82             });
           83             return maxPercent;
           84         } else {
           85             return this.getClient('toFillPercent');
           86         }
           87     },
           88     // 重載獲取網元名稱方法,判斷如果是鏈路捆綁代理,就返回起始和結束代理節點的名稱
           89     getName: function () {
           90         if (this.getClient('shortened')) {
           91             return null;
           92         } else if (this.isBundleAgent()) {
           93             return this.getFromAgent().getName() + '-' + this.getToAgent().getName();
           94         } else {
           95             return demo.ScaleLink.superClass.getName.call(this);
           96         }
           97     }
           98 });
           99 
          100 // 自定義LinkUI構造函數
          101 demo.ScaleLinkUI = function(network, element){
          102     // 調用基類構造函數
          103     demo.ScaleLinkUI.superClass.constructor.call(this, network, element);
          104 };
          105 // 設置自定義Link繼承twaver.canvas.LinkUI
          106 twaver.Util.ext('demo.ScaleLinkUI', twaver.canvas.LinkUI, {
          107     // 獲取Link角度
          108     getAngle: function () {
          109         return getAngle(this.getFromPoint(), this.getToPoint());
          110     },
          111     // 獲取Link中間點
          112     getMiddlePoint: function (from, to, percent) {
          113         return {
          114             x: from.x + (to.x - from.x) * percent,
          115             y: from.y + (to.y - from.y) * percent
          116         };
          117     },
          118     // 畫刻度線
          119     drawScaleLine: function (from, to, angle, length, ctx, percent, lineWidth, lineColor) {
          120         var point = this.getMiddlePoint(from, to, percent);
          121         var y = length/2 * Math.sin(angle),
          122             x = length/2 * Math.cos(angle);
          123         ctx.beginPath();
          124         ctx.lineWidth = lineWidth;
          125         ctx.strokeStyle = lineColor;
          126         ctx.moveTo(point.x + x, point.y + y);
          127         ctx.lineTo(point.x - x, point.y -y);
          128         ctx.stroke();
          129     },
          130     // 獲取是否將鏈路變短
          131     isShorten: function () {
          132         var link = this.getElement();
          133         return link.getClient('shortened') && this.getLineLength() > link.getClient('shortenLength') * 2;
          134     },
          135     // 重載畫鏈路函數,用自定義邏輯畫鏈路
          136     paintBody: function (ctx) {
          137         var points = this.getLinkPoints(),
          138             link = this.getElement();
          139         if (!points || points.size() < 2) {
          140             return;
          141         }
          142 
          143         var lineLength = this.getLineLength(),
          144             shortenLength = link.getClient('shortenLength'),
          145             percent = shortenLength / lineLength,
          146             from = points.get(0),
          147             to = points.get(1),
          148             angle = this.getAngle() + Math.PI/2;
          149         if (this.isShorten()) {
          150             fromPoints = new twaver.List([from, this.getMiddlePoint(from, to, percent)]);
          151             toPoints = new twaver.List([this.getMiddlePoint(from, to, 1 - percent), to]);
          152             this._paintBody(ctx, fromPoints, angle);
          153             this._paintBody(ctx, toPoints, angle);
          154 
          155             // 畫文字
          156             ctx.textAlign = 'center';
          157             ctx.textBaseline = 'middle';
          158             ctx.fillStyle = 'black';
          159             var textCenter = {x: (fromPoints.get(0).x + fromPoints.get(1).x)/2, y: (fromPoints.get(0).y + fromPoints.get(1).y)/2};
          160             ctx.fillText(link.getName(), textCenter.x, textCenter.y);
          161 
          162             textCenter = {x: (toPoints.get(0).x + toPoints.get(1).x)/2, y: (toPoints.get(0).y + toPoints.get(1).y)/2};
          163             ctx.fillText(link.getName(), textCenter.x, textCenter.y);
          164 
          165             ctx.fillText(link.getToNode().getName(), fromPoints.get(1).x, fromPoints.get(1).y);
          166             ctx.fillText(link.getFromNode().getName(), toPoints.get(0).x, toPoints.get(0).y);
          167         } else {
          168             this._paintBody(ctx, points, angle);
          169         }
          170 
          171         // 畫起始箭頭
          172         if (link.getClient('arrow.from')) {
          173             twaver.Util.drawArrow(ctx, 12, 9, points, true, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
          174         }
          175         // 畫結束箭頭
          176         if (link.getClient('arrow.to')) {
          177             twaver.Util.drawArrow(ctx, 12, 9, points, false, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
          178         }
          179     },
          180     _paintBody: function (ctx, points, angle) {
          181         var link = this.getElement(),
          182             width = link.getStyle('link.width'),
          183             grow = width,
          184             outerColor = this.getOuterColor();
          185         if (outerColor) {
          186             var outerWidth = link.getStyle('outer.width');
          187             grow += outerWidth * 2;
          188         }
          189         var selectBorder = !this.getEditAttachment() && link.getStyle('select.style') === 'border' && this.getNetwork().isSelected(link);
          190         if (selectBorder) {
          191             var selectWidth = link.getStyle('select.width');
          192             grow += selectWidth * 2;
          193         }
          194         ctx.lineCap = link.getStyle('link.cap');
          195         ctx.lineJoin = link.getStyle('link.join');
          196         // 畫選中邊框
          197         if (selectBorder) {
          198             this.drawLinePoints(ctx, points, grow, link.getStyle('select.color'));
          199         }
          200         // 畫邊框
          201         if (outerColor) {
          202             this.drawLinePoints(ctx, points, width + outerWidth * 2, outerColor);
          203         }
          204         // 畫Link
          205         this.drawLinePoints(ctx, points, width, this.getInnerColor() || link.getStyle('link.color'));
          206 
          207         var fromFillPercent = link.getFromFillPercent(),
          208             toFillPercent = link.getToFillPercent(),
          209             fromFillColor = link.getFromFillColor(),
          210             toFillColor = link.getToFillColor(),
          211             from = points.get(0),
          212             to = points.get(1);
          213 
          214         var x = from.x + (to.x - from.x) / 2 * fromFillPercent,
          215             y = from.y + (to.y - from.y) / 2 * fromFillPercent;
          216         var middle = {x: x, y: y};
          217         var fromPoints = new twaver.List([from, middle]);
          218         // 畫起始填充色
          219         this.drawLinePoints(ctx, fromPoints, width, fromFillColor);
          220 
          221         from = points.get(1);
          222         to = points.get(0);
          223         x = from.x + (to.x - from.x) / 2 * toFillPercent;
          224         y = from.y + (to.y - from.y) / 2 * toFillPercent;
          225         middle = {x: x, y: y};
          226         var toPoints = new twaver.List([from, middle]);
          227         // 畫結束填充色
          228         this.drawLinePoints(ctx, toPoints, width, toFillColor);
          229 
          230         from = points.get(0);
          231         to = points.get(1);
          232         var scaleWidth = link.getClient('scaleWidth'),
          233             scaleColor = link.getClient('scaleColor');
          234         // 畫刻度
          235         for (var i = 1, n = link.getClient('scaleNumbers') * 2; i < n; i++) {
          236             this.drawScaleLine(from, to, angle, width/2, ctx, i/n, scaleWidth, scaleColor);
          237         }
          238         // 畫分隔線
          239         this.drawScaleLine(from, to, angle, width, ctx, 0.5, 3, link.getClient('splitterColor'));
          240     }
          241 });

          最后一個需求可以通過定制Node和NodeUI達到目的:

            1 // 自定義Node構造函數
            2 demo.ScaleNode = function(id) {
            3     // 調用基類構造函數
            4     demo.ScaleNode.superClass.constructor.call(this, id);
            5 };
            6 // 設置自定義Node繼承twaver.Node
            7 twaver.Util.ext('demo.ScaleNode', twaver.Node, {
            8     getCanvasUIClass: function () {
            9         return demo.ScaleNodeUI;
           10     }
           11 });
           12 
           13 // 自定義NodeUI構造函數
           14 demo.ScaleNodeUI = function(network, element){
           15     // 調用基類構造函數
           16     demo.ScaleNodeUI.superClass.constructor.call(this, network, element);
           17 };
           18 // 設置自定義NodeUI繼承twaver.canvas.NodeUI
           19 twaver.Util.ext('demo.ScaleNodeUI', twaver.canvas.NodeUI, {
           20     // 重載畫網元方法,畫上層鏈路
           21     paintBody: function (ctx) {
           22         demo.ScaleNodeUI.superClass.paintBody.call(this, ctx);
           23         var result = this.getAttachedLinks();
           24         if (!result) {
           25             return;
           26         }
           27         for (var position in result) {
           28             this.paintLink(ctx, result[position], position);
           29         }
           30     },
           31     // 畫鏈路
           32     paintLink: function (ctx, links, position) {
           33         var center = this.getElement().getCenterLocation(),
           34             count = links.length,
           35             half = count / 2,
           36             network = this.getNetwork(),
           37             gap = (count - 1) * -10,
           38             terminal, link, i, offset, shortenLength, angle, tempCenter, textWidth, textHeight = 20, textCenter;
           39         for (i=0; i<count; i++) {
           40             link = links[i];
           41             offset = link.getStyle('link.bundle.offset');
           42             shortenLength = link.getClient('shortenLength');
           43             textWidth = ctx.measureText(link.getName()).width;
           44             if (position === 'left') {
           45                 terminal = {x: center.x - offset - shortenLength, y: center.y + gap};
           46                 tempCenter = {x: center.x - offset, y: center.y + gap};
           47                 textCenter = {x: terminal.x - textWidth/2 - 10, y: terminal.y};
           48                 angle = Math.PI/2;
           49             } else if (position === 'right') {
           50                 terminal = {x: center.x + offset + shortenLength, y: center.y + gap};
           51                 tempCenter = {x: center.x + offset, y: center.y + gap};
           52                 textCenter = {x: terminal.x + textWidth/2 + 10, y: terminal.y};
           53                 angle = Math.PI/2;
           54             } else if (position === 'top') {
           55                 terminal = {x: center.x + gap, y: center.y - offset - shortenLength};
           56                 tempCenter = {x: center.x + gap, y: center.y - offset};
           57                 textCenter = {x: terminal.x, y: terminal.y - 10};
           58                 angle = 0;
           59             } else {
           60                 terminal = {x: center.x + gap, y: center.y + offset + shortenLength};
           61                 tempCenter = {x: center.x + gap, y: center.y + offset};
           62                 textCenter = {x: terminal.x, y: terminal.y + 10};
           63                 angle = 0;
           64             }
           65             gap += 20;
           66             var isFrom = link.getFromNode() === this.getElement(),
           67                 points;
           68             if (isFrom) {
           69                 points = new twaver.List([tempCenter, terminal]);
           70             } else {
           71                 points = new twaver.List([terminal, tempCenter]);
           72             }
           73             network.getElementUI(link)._paintBody(ctx, points, angle);
           74 
           75             ctx.textAlign = 'center';
           76             ctx.textBaseline = 'middle';
           77             ctx.fillStyle = 'black';
           78             // 另一端節點標簽
           79             var name = isFrom ? link.getToNode().getName() : link.getFromNode().getName();
           80             ctx.fillText(name, textCenter.x, textCenter.y);
           81             textCenter = {x: (tempCenter.x + terminal.x)/2, y: (tempCenter.y + terminal.y)/2};
           82             // Link標簽
           83             ctx.fillText(link.getName(), textCenter.x, textCenter.y);
           84 
           85             // 畫起始箭頭
           86             if (link.getClient('arrow.from')) {
           87                 twaver.Util.drawArrow(ctx, 12, 9, points, true, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
           88             }
           89             // 畫結束箭頭
           90             if (link.getClient('arrow.to')) {
           91                 twaver.Util.drawArrow(ctx, 12, 9, points, false, 'arrow.standard', true, 'gray', 0, 0, 1, 'black');
           92             }
           93         }
           94     },
           95     // 獲取不同方位的上層鏈路集合
           96     getAttachedLinks: function () {
           97         var currentSubNetwork = this.getNetwork().getCurrentSubNetwork();
           98         if (!currentSubNetwork || !this.getElement().getLinks()) {
           99             return null;
          100         }
          101         var result;
          102         this.getElement().getLinks().forEach(function (link) {
          103             var fromSubNetwork = twaver.Util.getSubNetwork(link.getFromNode()),
          104                 toSubNetwork = twaver.Util.getSubNetwork(link.getToNode());
          105             if (fromSubNetwork !== toSubNetwork) {
          106                 if (!result) {
          107                     result = {};
          108                 }
          109                 var fromCenter = link.getFromNode().getCenterLocation(),
          110                     toCenter = link.getToNode().getCenterLocation(),
          111                     angle = getAngle(fromCenter, toCenter),
          112                     isOut = currentSubNetwork === fromSubNetwork,
          113                     position;
          114                 if (isOut) {
          115                     if (fromCenter.x <= toCenter.x) {
          116                         if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
          117                             position = 'right';
          118                         } else if (angle > Math.PI/4) {
          119                             position = 'bottom';
          120                         } else {
          121                             position = 'top';
          122                         }
          123                     } else {
          124                         if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
          125                             position = 'left';
          126                         } else if (angle > Math.PI/4) {
          127                             position = 'top';
          128                         } else {
          129                             position = 'bottom';
          130                         }
          131                     }
          132                 } else {
          133                     if (fromCenter.x <= toCenter.x) {
          134                         if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
          135                             position = 'left';
          136                         } else if (angle > Math.PI/4) {
          137                             position = 'top';
          138                         } else {
          139                             position = 'bottom';
          140                         }
          141                     } else {
          142                         if (angle >= -Math.PI/4 && angle <= Math.PI/4) {
          143                             position = 'right';
          144                         } else if (angle > Math.PI/4) {
          145                             position = 'bottom';
          146                         } else {
          147                             position = 'top';
          148                         }
          149                     }
          150                 }
          151                 if (!result[position]) {
          152                     result[position] = [];
          153                 }
          154                 result[position].push(link);
          155             }
          156         });
          157         return result;
          158     }
          159 });

           本文完整代碼見附件:見原文最下方

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 新龙县| 潮州市| 临城县| 儋州市| 凌海市| 高清| 郁南县| 临夏县| 望江县| 南召县| 万山特区| 易门县| 哈巴河县| 星子县| 淮滨县| 安乡县| 上思县| 望谟县| 阳春市| 霍山县| 嘉善县| 永宁县| 凤庆县| 连江县| 巴里| 江北区| 九江县| 铁力市| 德阳市| 芮城县| 新邵县| 达孜县| 额尔古纳市| 桐乡市| 衡山县| 台东县| 同江市| 长兴县| 澄迈县| 江达县| 罗平县|