用canvas绘制的osworkflow流程显示

这个是一个兼容osworkflow流程设计器设计出来的.xml和.lyt文件的显示流程图的js 
使用了jquery和excanvas.js兼容IE、Fiefox、Chrome、Opera浏览器 
支持鼠标事件 

 

解压src.zip后,打开demo.html点击生成流程图就可以观看演示效果 
 
 

Java代码 

 收藏代码

  1. (function() {  
  2.   
  3.     /** 
  4.      * 流程图绘制 
  5.      * @param canvas 
  6.      * @param xmlStr 
  7.      * @param lytStr 
  8.      * @param currentSteps 
  9.      * @param historySteps 
  10.      * @returns {WfCanvas} 
  11.      */  
  12.     function WfCanvas(canvas, xmlStr, lytStr, currentSteps, historySteps) {  
  13.         currentSteps = currentSteps || [];  
  14.         historySteps = historySteps || [];  
  15.         var   
  16.             cvs = $(canvas),  
  17.             //bcolor = $(canvas).css("background-color"),  
  18.             fontFamily = "Arial",  
  19.             fontSize = 15,  
  20.               
  21.             font = "normal normal bold " + fontSize + "px " + fontFamily,  
  22.             labelFontSize = 12,  
  23.             labelFont = "normal normal normal " + labelFontSize + "px " + fontFamily,  
  24.             fontColor = "Black",  
  25.             borderColor = "Black",  
  26.             stepColor = "#FFFF37",  
  27.             lineColor = "Black",  
  28.             shadowOffset = 6;  
  29.             initActionCorlor = "#9AFF02",  
  30.             currentStepColor = "#FF5151",  
  31.             historyStepColor = "#9AFF02",  
  32.             shadwColor = "#d0d0d0",  
  33.             ctx = null,  
  34.             xml = null,  
  35.             lyt = null,  
  36.             xmlNodeMap = {},  
  37.             cellNodeMap = {},  
  38.             connectorNodeMap = {},  
  39.             cellMap = {},  
  40.             connectorMap = {};  
  41.   
  42.         /** 
  43.          * 画流程图 
  44.          */  
  45.         function drawWf() {  
  46.             init();  
  47.             mouseSupport();  
  48.             draw();  
  49.         }  
  50.   
  51.         function init() {  
  52.               
  53.             if(!canvas.getContext) {  
  54.                 window.G_vmlCanvasManager.initElement(canvas);    
  55.             }  
  56.             ctx = canvas.getContext("2d");  
  57.   
  58.             xml = $.parseXML(xmlStr);  
  59.             lyt = $.parseXML(lytStr);  
  60.   
  61.             $.each($(xml).find("[id]"), function(i, node) {  
  62.                 xmlNodeMap[$(node).attr("id")] = node;  
  63.             });  
  64.   
  65.             $.each($(lyt).find("[id]"), function(i, node) {  
  66.                 if (node.tagName == "cell") {  
  67.                     cellNodeMap[$(node).attr("id")] = node;  
  68.                 } else if (node.tagName == "connector") {  
  69.                     connectorNodeMap[$(node).attr("id")] = node;  
  70.                 }  
  71.             });  
  72.   
  73.             $.each(cellNodeMap, function(id, cellNode){  
  74.                 cellNode = $(cellNode);  
  75.                 var xmlNode = $(xmlNodeMap[id]);  
  76.                 var isCurrent = false;  
  77.                 var isHistory = false;  
  78.                 $.each(currentSteps, function(idx, stepId){  
  79.                     if (stepId == id) {  
  80.                         isCurrent = true;  
  81.                     }  
  82.                 });  
  83.                 $.each(historySteps, function(idx, stepId){  
  84.                     if (stepId == id) {  
  85.                         isHistory = true;  
  86.                     }  
  87.                 });  
  88.                 cellMap[id] = {  
  89.                     id: id,  
  90.                     name: xmlNode.attr("name"),  
  91.                     type : cellNode.attr("type"),  
  92.                     height: parseFloat(cellNode.attr("height")),  
  93.                     width: parseFloat(cellNode.attr("width")),  
  94.                     labelx: parseFloat(cellNode.attr("labelx")),  
  95.                     labely: parseFloat(cellNode.attr("labely")),  
  96.                     x: parseFloat(cellNode.attr("x")),  
  97.                     y: parseFloat(cellNode.attr("y")),  
  98.                     isCurrent: isCurrent,  
  99.                     isHistory: isHistory  
  100.                 };  
  101.             });  
  102.   
  103.             $.each(connectorNodeMap, function(id, connectorNode){  
  104.                 connectorNode = $(connectorNode);  
  105.                 var xmlNode = $(xmlNodeMap[id]);  
  106.                 var actionNode = xmlNode.parent().parent();  
  107.                 var stepNode = actionNode.parent().parent();  
  108.                 var srcId = stepNode.attr("id");  
  109.                 if (actionNode.parent()[0].tagName == "initial-actions") {  
  110.                     srcId = actionNode.attr("id");  
  111.                 }  
  112.                 var routings = [];  
  113.                 var routingNodes = connectorNode.children("routing");  
  114.                 $.each(routingNodes, function(idx, routingNode) {  
  115.                     routingNode = $(routingNode);  
  116.                     var routing = {  
  117.                         x: parseFloat(routingNode.attr("x")),  
  118.                         y: parseFloat(routingNode.attr("y"))  
  119.                     };  
  120.                     routings.push(routing);  
  121.                 });  
  122.                 connectorMap[id] = {  
  123.                     id: id,  
  124.                     label: actionNode.attr("name"),  
  125.                     color : connectorNode.attr("color"),  
  126.                     linewidth: parseFloat(connectorNode.attr("linewidth")),  
  127.                     labelx: parseFloat(connectorNode.attr("labelx")),  
  128.                     labely: parseFloat(connectorNode.attr("labely")),  
  129.                     from: parseInt(connectorNode.attr("from")),  
  130.                     to: parseInt(connectorNode.attr("to")),  
  131.                     src: cellMap[srcId],  
  132.                     target: cellMap[xmlNode.attr("step")],  
  133.                     routings: routings  
  134.                 };  
  135.             });  
  136.   
  137.             var size = calculateCanvasSize();  
  138.             //cvs.width(size.width);  
  139.             //cvs.height(size.height);  
  140.             canvas.width = size.width + 10;  
  141.             canvas.height = size.height + 10;  
  142.         }  
  143.   
  144.         function calculateCanvasSize() {  
  145.             var minx = 0, miny = 0, maxx = 0, maxy=0;  
  146.             $.each(cellMap, function(id, cell) {  
  147.                 minx = Math.min(minx, cell.x);  
  148.                 miny = Math.min(miny, cell.y);  
  149.                 maxx = Math.max(maxx, cell.x + cell.width);  
  150.                 maxy = Math.max(maxy, cell.y + cell.height);  
  151.             });  
  152.             $.each(connectorMap, function(id, connector) {  
  153.                 $.each(connector.routings, function(i, r) {  
  154.                     minx = Math.min(minx, r.x);  
  155.                     miny = Math.min(miny, r.y);  
  156.                     maxx = Math.max(maxx, r.x);  
  157.                     maxy = Math.max(maxy, r.y);  
  158.                 });  
  159.             });  
  160.             return {  
  161.                 width: maxx - minx,  
  162.                 height: maxy - miny  
  163.             };  
  164.         }  
  165.   
  166.         function mouseSupport() {  
  167.             cvs.unbind("mousemove").mousemove(function(e) {  
  168.                 $.each(cellMap, function(id, cell){  
  169.                     if (!cell.isHover) {  
  170.                         if (isInRange(e, cell)) {  
  171.                             cell.isHover = true;  
  172.                             cvs.css("cursor", "pointer");  
  173.                         } else {  
  174.                             cell.isHover = false;  
  175.                         }  
  176.                     } else {  
  177.                         if (!isInRange(e, cell)) {  
  178.                             cell.isHover = false;  
  179.                             cvs.css("cursor", "default");  
  180.                         } else {  
  181.                             cell.isHover = true;  
  182.                         }  
  183.                     }  
  184.                 });  
  185.             });  
  186.             cvs.unbind("click").click(function(e) {  
  187.                 $.each(cellMap, function(id, cell){  
  188.                     if (isInRange(e, cell)) {  
  189.                         alert(cell.name);  
  190.                     }  
  191.                 });  
  192.             });  
  193.         }  
  194.           
  195.         function isInRange(e, cell) {  
  196.             var x = e.offsetX || e.clientX - cvs.offset().left,  
  197.                 y = e.offsetY || e.clientY - cvs.offset().top;  
  198.               
  199.             if (x >= cell.x && x <= cell.x + cell.width   
  200.                     && y >= cell.y && y <= cell.y + cell.height) {  
  201.                 return true;  
  202.             }  
  203.               
  204.             return false;  
  205.         }  
  206.           
  207.         /** 
  208.          * 画流程图 
  209.          */  
  210.         function draw() {  
  211.             ctx.clearRect(0, 0, cvs.width(), cvs.height());  
  212.             $.each(cellMap, function(id, cell) {  
  213.                 drawCell(cell);  
  214.             });  
  215.             $.each(connectorMap, function(id, connector) {  
  216.                 drawConnector(connector);  
  217.             });  
  218.         }  
  219.   
  220.         /** 
  221.          * 画步骤 
  222.          * @param cell 
  223.          */  
  224.         function drawCell(cell) {  
  225.             ctx.strokeStyle = borderColor;  
  226.             if (cell.type == "InitialActionCell") {  
  227.                 ctx.fillStyle = initActionCorlor;  
  228.                 drawEllipse(cell, cell.width, cell.height);  
  229.             } else {  
  230.                 if (cell.isCurrent) {  
  231.                     ctx.fillStyle = currentStepColor;  
  232.                 } else if (cell.isHistory){  
  233.                     ctx.fillStyle = historyStepColor;  
  234.                 } else {  
  235.                     ctx.fillStyle = stepColor;  
  236.                 }  
  237.                 drawRect(cell, cell.width, cell.height);  
  238.             }  
  239.               
  240.             var fx = cell.x + (cell.width - cell.name.length * fontSize) / 2;  
  241.             var fy = cell.y + (cell.height - fontSize) / 2 + fontSize;  
  242.   
  243.             ctx.font = font;  
  244.             ctx.fillStyle = fontColor;  
  245.             ctx.fillText(cell.name, fx, fy);  
  246.         }  
  247.   
  248.         /** 
  249.          * 画连接 
  250.          * @param c 
  251.          */  
  252.         function drawConnector(c) {  
  253.             var points = [];  
  254.             // 起点  
  255.             points.push(getPortPoint(c.src, c.from));  
  256.             // 中间点  
  257.             $.each(c.routings, function(idx, routing) {  
  258.                 points.push(routing);  
  259.             });  
  260.             // 终点  
  261.             points.push(getPortPoint(c.target, c.to));  
  262.               
  263.             var last = points.length - 1;  
  264.             // 如果两端是cell的中点,就获取与cell相交的点  
  265.             if (c.from == 0) {  
  266.                 points[0] = getCrossPoint(c.src, points[1], points[0]);  
  267.             };  
  268.             if (c.to == 0) {  
  269.                 points[last] = getCrossPoint(c.target, points[last-1], points[last]);  
  270.             };  
  271.   
  272.             // 画线  
  273.             ctx.strokeStyle = lineColor;  
  274.             for (var i = 0; i < last; i++) {  
  275.                 drawLine(points[i], points[i+1]);  
  276.             }  
  277.               
  278.             // 画文字  
  279.             var dx = points[last].x - points[0].x,  
  280.                 dy = points[last].y - points[0].y,  
  281.                 fx = c.labelx / 1000 * dx + points[0].x,  
  282.                 fy = c.labely / 1000 * dy + points[0].y,  
  283.                 fw = c.label.length * labelFontSize,  
  284.                 fh = labelFontSize;  
  285.               
  286.             fx -= fw / 2;  
  287.             fy += fh / 2;  
  288.               
  289.             //ctx.fillStyle = bcolor;  
  290.             //ctx.fillRect(fx, fy, fw, fh);  
  291.             //ctx.strokeRect(fx, fy, fw, fh);  
  292.               
  293.             ctx.font = labelFont;  
  294.             ctx.fillStyle = fontColor;  
  295.             ctx.fillText(c.label, fx, fy);  
  296.               
  297.             // 画箭头  
  298.             drawArrow(points[last-1], points[last]);  
  299.   
  300.         };  
  301.   
  302.         /** 
  303.          * 画圆角方块 
  304.          * @param p 坐标点 
  305.          * @param w 宽度 
  306.          * @param h 高度 
  307.          * @param fill 是否填充 
  308.          * @param stroke 是否画线 
  309.          * @param drawShadow 是否画阴影 
  310.          */  
  311.         function drawRect(p, w, h, fill, stroke, drawShadow) {  
  312.             fill = typeof(fill) == "undefined" ? true : fill;  
  313.             stroke = typeof(stroke) == "undefined" ? true : stroke;  
  314.             drawShadow = typeof(drawShadow) == "undefined" ? true : drawShadow;  
  315.             if (drawShadow) {  
  316.                 var offset = shadowOffset;  
  317.                 var oldStyle = ctx.fillStyle;  
  318.                 ctx.fillStyle = shadwColor;  
  319.                 drawRect({x: p.x + offset, y: p.y + offset}, w, h, true, false, false);  
  320.                 ctx.fillStyle = oldStyle;  
  321.             }  
  322.   
  323.             var x = p.x,  
  324.                 y = p.y,  
  325.                 r = 5; // 圆角半径  
  326.             if (w < 2 * r) {  
  327.                 r = w / 2;  
  328.             }  
  329.             if (h < 2 * r) {  
  330.                 r = h / 2;  
  331.             }  
  332.             ctx.beginPath();  
  333.             ctx.moveTo(x + r, y);  
  334.             ctx.lineTo(x + w - r, y);  
  335.             ctx.quadraticCurveTo(x + w, y, x + w, y + r);  
  336.             ctx.lineTo(x + w, y + h - r);  
  337.             ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);  
  338.             ctx.lineTo(x + r, y + h);  
  339.             ctx.quadraticCurveTo(x, y + h, x, y + h - r);  
  340.             ctx.lineTo(x, y + r);  
  341.             ctx.quadraticCurveTo(x, y, x + r, y);  
  342.             ctx.closePath();  
  343.             if (stroke) {  
  344.                 ctx.stroke();  
  345.             }  
  346.             if (fill) {  
  347.                 ctx.fill();  
  348.             }   
  349.         }  
  350.   
  351.         /** 
  352.          * 画椭圆 
  353.          * @param p 坐标点 
  354.          * @param w 宽度 
  355.          * @param h 高度 
  356.          * @param fill 是否填充 
  357.          * @param stroke 是否画线 
  358.          * @param drawShadow 是否画阴影 
  359.          */  
  360.         function drawEllipse(p, w, h, fill, stroke, drawShadow) {  
  361.             fill = typeof(fill) == "undefined" ? true : fill;  
  362.             stroke = typeof(stroke) == "undefined" ? true : stroke;  
  363.             drawShadow = typeof(drawShadow) == "undefined" ? true : drawShadow;  
  364.             if (drawShadow) {  
  365.                 var offset = shadowOffset;  
  366.                 var oldStyle = ctx.fillStyle;  
  367.                 ctx.fillStyle = shadwColor;  
  368.                 drawEllipse({x: p.x + offset, y: p.y + offset}, w, h, true, false, false);  
  369.                 ctx.fillStyle = oldStyle;  
  370.             }  
  371.             var k = 0.5522848,  
  372.                 a = w / 2,  
  373.                 b = h / 2,  
  374.                 ox = a * k, // 水平控制点偏移量  
  375.                 oy = b * k, // 垂直控制点偏移量  
  376.                 x = p.x + a, y = p.y + b;  
  377.             ctx.beginPath();  
  378.             // 从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线  
  379.             ctx.moveTo(x - a, y);  
  380.             ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);  
  381.             ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);  
  382.             ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);  
  383.             ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);  
  384.             ctx.closePath();  
  385.             if (fill) {  
  386.                 ctx.fill();  
  387.             }  
  388.             if (stroke) {  
  389.                 ctx.stroke();  
  390.             }  
  391.         }  
  392.   
  393.         function getCrossPoint(cell, p1, p2) {  
  394.             var x = p2.x,   
  395.                 y = p2.y,  
  396.                 dx = p2.x - p1.x,  
  397.                 dy = p2.y - p1.y;  
  398.             if (dx == 0 && dy == 0) {  
  399.                 return p2;  
  400.             }  
  401.             if (Math.abs(dy) > 0 && Math.abs(dx) / Math.abs(dy) < cell.width / cell.height) {  
  402.                 var tan = dx / dy;  
  403.                 if (p2.y < p1.y) {  
  404.                     x += cell.height / 2 * tan;  
  405.                     y += cell.height / 2;  
  406.                 } else {  
  407.                     x -= cell.height / 2 * tan;  
  408.                     y -= cell.height / 2;  
  409.                 }  
  410.             } else {  
  411.                 var tan = Math.abs(dx) > 0 ? dy / dx : 1;  
  412.                 if (p2.x < p1.x) {  
  413.                     x += cell.width / 2;  
  414.                     y += cell.width / 2 * tan;  
  415.                 } else {  
  416.                     x -= cell.width / 2;  
  417.                     y -= cell.width / 2 * tan;  
  418.                 }  
  419.             }  
  420.             return {x: x, y: y};  
  421.         }  
  422.   
  423.         function getPortPoint(cell, no) {  
  424.             var x = cell.x,  
  425.                 y = cell.y;  
  426.             switch(no) {  
  427.             case 1:   
  428.                 x = cell.x;  
  429.                 y = cell.y;  
  430.                 break;  
  431.             case 2:  
  432.                 x += cell.width / 2;  
  433.                 break;  
  434.             case 3:  
  435.                 x += cell.width;  
  436.                 break;  
  437.             case 4:  
  438.                 y += cell.height / 2;  
  439.                 break;  
  440.             case 5:  
  441.                 x += cell.width;  
  442.                 y += cell.height / 2;  
  443.                 break;  
  444.             case 6:  
  445.                 y += cell.height;  
  446.                 break;  
  447.             case 7:  
  448.                 x += cell.width / 2;  
  449.                 y += cell.height;  
  450.                 break;  
  451.             case 8:  
  452.                 x += cell.width;  
  453.                 y += cell.height;  
  454.                 break;  
  455.             case 0:  
  456.                 x += cell.width / 2;  
  457.                 y += cell.height / 2;  
  458.                 break;  
  459.             }  
  460.             return {x:x, y:y};  
  461.         }  
  462.   
  463.         /** 
  464.          * 画线 
  465.          * @param p1 
  466.          * @param p2 
  467.          */  
  468.         function drawLine(p1, p2) {  
  469.             ctx.beginPath();  
  470.             ctx.moveTo(p1.x, p1.y);  
  471.             ctx.lineTo(p2.x, p2.y);  
  472.             ctx.stroke();  
  473.         }  
  474.   
  475.         /** 
  476.          * 画箭头 
  477.          * @param p1 
  478.          * @param p2 
  479.          */  
  480.         function drawArrow(p1, p2) {  
  481.             var   
  482.                 awrad = Math.PI / 6, // 箭头角度(30度)  
  483.                 arrowLen = 10,      // 箭头长度  
  484.                 ap0 = toRelative(p1, p2), // 旋转源点(line.p1相对于line.p2的坐标)  
  485.                 ap1 = rotateVec(ap0, awrad, arrowLen), // 第一端点(相对于line.p2的坐标)  
  486.                 ap2 = rotateVec(ap0, -awrad, arrowLen); // 第二端点(相对于line.p2的坐标)  
  487.   
  488.             ap1 = toAbsolute(ap1, p2);  
  489.             ap2 = toAbsolute(ap2, p2);  
  490.   
  491.             drawLine(p2, ap1);  
  492.             drawLine(p2, ap2);  
  493.         }  
  494.   
  495.         // 转换成相对坐标  
  496.         function toRelative(p, p0) {  
  497.             return {  
  498.                 x: p.x - p0.x,  
  499.                 y: p.y - p0.y  
  500.             };  
  501.         }  
  502.   
  503.         // 转换回绝对坐标  
  504.         function toAbsolute(p, p0) {  
  505.             return {  
  506.                 x: p.x + p0.x,  
  507.                 y: p.y + p0.y  
  508.             };  
  509.         }  
  510.   
  511.         /** 
  512.          * 矢量旋转函数 
  513.          * @param p 坐标源点 
  514.          * @param ang 旋转角 
  515.          * @param newLen 新长度 
  516.          * @returns {x,y} 
  517.          */  
  518.         function rotateVec(p, ang, newLen) {  
  519.             var vx = p.x * Math.cos(ang) - p.y * Math.sin(ang);  
  520.             var vy = p.x * Math.sin(ang) + p.y * Math.cos(ang);  
  521.             var d = Math.sqrt(vx * vx + vy * vy);  
  522.             if (Math.abs(d) > 0) {  
  523.                 vx = vx / d * newLen;  
  524.                 vy = vy / d * newLen;  
  525.             }  
  526.             return {  
  527.                 x : vx,  
  528.                 y : vy  
  529.             };  
  530.         }  
  531.   
  532.         /** 
  533.          * 计算一点相对于圆心旋转后的坐标 
  534.          * @param c 圆心 
  535.          * @param p 点 
  536.          * @param r 旋转弧度 
  537.          */  
  538.         function rotate(c, p, r) {  
  539.             return {  
  540.                 x: (p.x - c.x) * Math.cos(r) - (p.y - c.y) * Math.sin(r) + c.x,  
  541.                 y: (p.y - c.y) * Math.cos(r) + (p.x - c.x) * sin(r) + c.y  
  542.             };  
  543.         }  
  544.           
  545.         $.extend(this, {  
  546.             drawWf: drawWf  
  547.         });  
  548.     }  
  549.       
  550.     window.drawWf = function(canvas, xmlStr, lytStr, currentSteps, historySteps) {  
  551.         var wf = new WfCanvas(canvas, xmlStr, lytStr, currentSteps, historySteps);  
  552.   
  553.         wf.drawWf();  
  554.     };  
  555. })();  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值