浏览 5379 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2013-06-03
最后修改:2013-06-03
使用了jquery和excanvas.js兼容IE、Fiefox、Chrome、Opera浏览器 支持鼠标事件 ![]() 解压src.zip后,打开demo.html点击生成流程图就可以观看演示效果 ![]() (function() { /** * 流程图绘制 * @param canvas * @param xmlStr * @param lytStr * @param currentSteps * @param historySteps * @returns {WfCanvas} */ function WfCanvas(canvas, xmlStr, lytStr, currentSteps, historySteps) { currentSteps = currentSteps || []; historySteps = historySteps || []; var cvs = $(canvas), //bcolor = $(canvas).css("background-color"), fontFamily = "Arial", fontSize = 15, font = "normal normal bold " + fontSize + "px " + fontFamily, labelFontSize = 12, labelFont = "normal normal normal " + labelFontSize + "px " + fontFamily, fontColor = "Black", borderColor = "Black", stepColor = "#FFFF37", lineColor = "Black", shadowOffset = 6; initActionCorlor = "#9AFF02", currentStepColor = "#FF5151", historyStepColor = "#9AFF02", shadwColor = "#d0d0d0", ctx = null, xml = null, lyt = null, xmlNodeMap = {}, cellNodeMap = {}, connectorNodeMap = {}, cellMap = {}, connectorMap = {}; /** * 画流程图 */ function drawWf() { init(); mouseSupport(); draw(); } function init() { if(!canvas.getContext) { window.G_vmlCanvasManager.initElement(canvas); } ctx = canvas.getContext("2d"); xml = $.parseXML(xmlStr); lyt = $.parseXML(lytStr); $.each($(xml).find("[id]"), function(i, node) { xmlNodeMap[$(node).attr("id")] = node; }); $.each($(lyt).find("[id]"), function(i, node) { if (node.tagName == "cell") { cellNodeMap[$(node).attr("id")] = node; } else if (node.tagName == "connector") { connectorNodeMap[$(node).attr("id")] = node; } }); $.each(cellNodeMap, function(id, cellNode){ cellNode = $(cellNode); var xmlNode = $(xmlNodeMap[id]); var isCurrent = false; var isHistory = false; $.each(currentSteps, function(idx, stepId){ if (stepId == id) { isCurrent = true; } }); $.each(historySteps, function(idx, stepId){ if (stepId == id) { isHistory = true; } }); cellMap[id] = { id: id, name: xmlNode.attr("name"), type : cellNode.attr("type"), height: parseFloat(cellNode.attr("height")), width: parseFloat(cellNode.attr("width")), labelx: parseFloat(cellNode.attr("labelx")), labely: parseFloat(cellNode.attr("labely")), x: parseFloat(cellNode.attr("x")), y: parseFloat(cellNode.attr("y")), isCurrent: isCurrent, isHistory: isHistory }; }); $.each(connectorNodeMap, function(id, connectorNode){ connectorNode = $(connectorNode); var xmlNode = $(xmlNodeMap[id]); var actionNode = xmlNode.parent().parent(); var stepNode = actionNode.parent().parent(); var srcId = stepNode.attr("id"); if (actionNode.parent()[0].tagName == "initial-actions") { srcId = actionNode.attr("id"); } var routings = []; var routingNodes = connectorNode.children("routing"); $.each(routingNodes, function(idx, routingNode) { routingNode = $(routingNode); var routing = { x: parseFloat(routingNode.attr("x")), y: parseFloat(routingNode.attr("y")) }; routings.push(routing); }); connectorMap[id] = { id: id, label: actionNode.attr("name"), color : connectorNode.attr("color"), linewidth: parseFloat(connectorNode.attr("linewidth")), labelx: parseFloat(connectorNode.attr("labelx")), labely: parseFloat(connectorNode.attr("labely")), from: parseInt(connectorNode.attr("from")), to: parseInt(connectorNode.attr("to")), src: cellMap[srcId], target: cellMap[xmlNode.attr("step")], routings: routings }; }); var size = calculateCanvasSize(); //cvs.width(size.width); //cvs.height(size.height); canvas.width = size.width + 10; canvas.height = size.height + 10; } function calculateCanvasSize() { var minx = 0, miny = 0, maxx = 0, maxy=0; $.each(cellMap, function(id, cell) { minx = Math.min(minx, cell.x); miny = Math.min(miny, cell.y); maxx = Math.max(maxx, cell.x + cell.width); maxy = Math.max(maxy, cell.y + cell.height); }); $.each(connectorMap, function(id, connector) { $.each(connector.routings, function(i, r) { minx = Math.min(minx, r.x); miny = Math.min(miny, r.y); maxx = Math.max(maxx, r.x); maxy = Math.max(maxy, r.y); }); }); return { width: maxx - minx, height: maxy - miny }; } function mouseSupport() { cvs.unbind("mousemove").mousemove(function(e) { $.each(cellMap, function(id, cell){ if (!cell.isHover) { if (isInRange(e, cell)) { cell.isHover = true; cvs.css("cursor", "pointer"); } else { cell.isHover = false; } } else { if (!isInRange(e, cell)) { cell.isHover = false; cvs.css("cursor", "default"); } else { cell.isHover = true; } } }); }); cvs.unbind("click").click(function(e) { $.each(cellMap, function(id, cell){ if (isInRange(e, cell)) { alert(cell.name); } }); }); } function isInRange(e, cell) { var x = e.offsetX || e.clientX - cvs.offset().left, y = e.offsetY || e.clientY - cvs.offset().top; if (x >= cell.x && x <= cell.x + cell.width && y >= cell.y && y <= cell.y + cell.height) { return true; } return false; } /** * 画流程图 */ function draw() { ctx.clearRect(0, 0, cvs.width(), cvs.height()); $.each(cellMap, function(id, cell) { drawCell(cell); }); $.each(connectorMap, function(id, connector) { drawConnector(connector); }); } /** * 画步骤 * @param cell */ function drawCell(cell) { ctx.strokeStyle = borderColor; if (cell.type == "InitialActionCell") { ctx.fillStyle = initActionCorlor; drawEllipse(cell, cell.width, cell.height); } else { if (cell.isCurrent) { ctx.fillStyle = currentStepColor; } else if (cell.isHistory){ ctx.fillStyle = historyStepColor; } else { ctx.fillStyle = stepColor; } drawRect(cell, cell.width, cell.height); } var fx = cell.x + (cell.width - cell.name.length * fontSize) / 2; var fy = cell.y + (cell.height - fontSize) / 2 + fontSize; ctx.font = font; ctx.fillStyle = fontColor; ctx.fillText(cell.name, fx, fy); } /** * 画连接 * @param c */ function drawConnector(c) { var points = []; // 起点 points.push(getPortPoint(c.src, c.from)); // 中间点 $.each(c.routings, function(idx, routing) { points.push(routing); }); // 终点 points.push(getPortPoint(c.target, c.to)); var last = points.length - 1; // 如果两端是cell的中点,就获取与cell相交的点 if (c.from == 0) { points[0] = getCrossPoint(c.src, points[1], points[0]); }; if (c.to == 0) { points[last] = getCrossPoint(c.target, points[last-1], points[last]); }; // 画线 ctx.strokeStyle = lineColor; for (var i = 0; i < last; i++) { drawLine(points[i], points[i+1]); } // 画文字 var dx = points[last].x - points[0].x, dy = points[last].y - points[0].y, fx = c.labelx / 1000 * dx + points[0].x, fy = c.labely / 1000 * dy + points[0].y, fw = c.label.length * labelFontSize, fh = labelFontSize; fx -= fw / 2; fy += fh / 2; //ctx.fillStyle = bcolor; //ctx.fillRect(fx, fy, fw, fh); //ctx.strokeRect(fx, fy, fw, fh); ctx.font = labelFont; ctx.fillStyle = fontColor; ctx.fillText(c.label, fx, fy); // 画箭头 drawArrow(points[last-1], points[last]); }; /** * 画圆角方块 * @param p 坐标点 * @param w 宽度 * @param h 高度 * @param fill 是否填充 * @param stroke 是否画线 * @param drawShadow 是否画阴影 */ function drawRect(p, w, h, fill, stroke, drawShadow) { fill = typeof(fill) == "undefined" ? true : fill; stroke = typeof(stroke) == "undefined" ? true : stroke; drawShadow = typeof(drawShadow) == "undefined" ? true : drawShadow; if (drawShadow) { var offset = shadowOffset; var oldStyle = ctx.fillStyle; ctx.fillStyle = shadwColor; drawRect({x: p.x + offset, y: p.y + offset}, w, h, true, false, false); ctx.fillStyle = oldStyle; } var x = p.x, y = p.y, r = 5; // 圆角半径 if (w < 2 * r) { r = w / 2; } if (h < 2 * r) { r = h / 2; } ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); if (stroke) { ctx.stroke(); } if (fill) { ctx.fill(); } } /** * 画椭圆 * @param p 坐标点 * @param w 宽度 * @param h 高度 * @param fill 是否填充 * @param stroke 是否画线 * @param drawShadow 是否画阴影 */ function drawEllipse(p, w, h, fill, stroke, drawShadow) { fill = typeof(fill) == "undefined" ? true : fill; stroke = typeof(stroke) == "undefined" ? true : stroke; drawShadow = typeof(drawShadow) == "undefined" ? true : drawShadow; if (drawShadow) { var offset = shadowOffset; var oldStyle = ctx.fillStyle; ctx.fillStyle = shadwColor; drawEllipse({x: p.x + offset, y: p.y + offset}, w, h, true, false, false); ctx.fillStyle = oldStyle; } var k = 0.5522848, a = w / 2, b = h / 2, ox = a * k, // 水平控制点偏移量 oy = b * k, // 垂直控制点偏移量 x = p.x + a, y = p.y + b; ctx.beginPath(); // 从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线 ctx.moveTo(x - a, y); ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b); ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y); ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b); ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y); ctx.closePath(); if (fill) { ctx.fill(); } if (stroke) { ctx.stroke(); } } function getCrossPoint(cell, p1, p2) { var x = p2.x, y = p2.y, dx = p2.x - p1.x, dy = p2.y - p1.y; if (dx == 0 && dy == 0) { return p2; } if (Math.abs(dy) > 0 && Math.abs(dx) / Math.abs(dy) < cell.width / cell.height) { var tan = dx / dy; if (p2.y < p1.y) { x += cell.height / 2 * tan; y += cell.height / 2; } else { x -= cell.height / 2 * tan; y -= cell.height / 2; } } else { var tan = Math.abs(dx) > 0 ? dy / dx : 1; if (p2.x < p1.x) { x += cell.width / 2; y += cell.width / 2 * tan; } else { x -= cell.width / 2; y -= cell.width / 2 * tan; } } return {x: x, y: y}; } function getPortPoint(cell, no) { var x = cell.x, y = cell.y; switch(no) { case 1: x = cell.x; y = cell.y; break; case 2: x += cell.width / 2; break; case 3: x += cell.width; break; case 4: y += cell.height / 2; break; case 5: x += cell.width; y += cell.height / 2; break; case 6: y += cell.height; break; case 7: x += cell.width / 2; y += cell.height; break; case 8: x += cell.width; y += cell.height; break; case 0: x += cell.width / 2; y += cell.height / 2; break; } return {x:x, y:y}; } /** * 画线 * @param p1 * @param p2 */ function drawLine(p1, p2) { ctx.beginPath(); ctx.moveTo(p1.x, p1.y); ctx.lineTo(p2.x, p2.y); ctx.stroke(); } /** * 画箭头 * @param p1 * @param p2 */ function drawArrow(p1, p2) { var awrad = Math.PI / 6, // 箭头角度(30度) arrowLen = 10, // 箭头长度 ap0 = toRelative(p1, p2), // 旋转源点(line.p1相对于line.p2的坐标) ap1 = rotateVec(ap0, awrad, arrowLen), // 第一端点(相对于line.p2的坐标) ap2 = rotateVec(ap0, -awrad, arrowLen); // 第二端点(相对于line.p2的坐标) ap1 = toAbsolute(ap1, p2); ap2 = toAbsolute(ap2, p2); drawLine(p2, ap1); drawLine(p2, ap2); } // 转换成相对坐标 function toRelative(p, p0) { return { x: p.x - p0.x, y: p.y - p0.y }; } // 转换回绝对坐标 function toAbsolute(p, p0) { return { x: p.x + p0.x, y: p.y + p0.y }; } /** * 矢量旋转函数 * @param p 坐标源点 * @param ang 旋转角 * @param newLen 新长度 * @returns {x,y} */ function rotateVec(p, ang, newLen) { var vx = p.x * Math.cos(ang) - p.y * Math.sin(ang); var vy = p.x * Math.sin(ang) + p.y * Math.cos(ang); var d = Math.sqrt(vx * vx + vy * vy); if (Math.abs(d) > 0) { vx = vx / d * newLen; vy = vy / d * newLen; } return { x : vx, y : vy }; } /** * 计算一点相对于圆心旋转后的坐标 * @param c 圆心 * @param p 点 * @param r 旋转弧度 */ function rotate(c, p, r) { return { x: (p.x - c.x) * Math.cos(r) - (p.y - c.y) * Math.sin(r) + c.x, y: (p.y - c.y) * Math.cos(r) + (p.x - c.x) * sin(r) + c.y }; } $.extend(this, { drawWf: drawWf }); } window.drawWf = function(canvas, xmlStr, lytStr, currentSteps, historySteps) { var wf = new WfCanvas(canvas, xmlStr, lytStr, currentSteps, historySteps); wf.drawWf(); }; })(); 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2013-06-05
目前好像都很少用osworkflow的了
|
|
返回顶楼 | |
发表时间:2014-03-18
非常不错.
|
|
返回顶楼 | |