因为工作需要在网页上实现流程图,不经意间发现了JointJS。JointJS是一款功能强大的图形可视化交互工具,可用来开发流程图,思维导图等复杂图形交互应用。废话不多说,直接上代码:
官网:https://resources.jointjs.com/demos
1、引入js,需要注意引入顺序
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/2.2.1/joint.min.js"></script>
2、创建画布及画板
const graph = new joint.dia.Graph; // 创建画板,所有图上的元素都在画板里 var paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板 el: document.getElementById('myGraph'), // 指向HTML里ID为"myGraph"的元素 model: graph, // 指定画板 width: 1500, // 画布宽600像素 height: 300, // 画布高100像素 gridSize: 1, // 画布上元素拖动时步进的像素,默认1,设的高方便对齐 background: { // 画布背景色 color: 'rgba(0, 0, 0, 0.1)' }, interactive :true //元素是否可拖动 });
3、Joint.js的图形元素
常见的图形如:矩形(Rectangle),多边形(Polygon),圆(Circle),椭圆(Ellipse)外,还可以插入图片(BorderedImage)等
// 创建一个矩形 var rect = new joint.shapes.standard.Rectangle(); rect.position(100, 30); // 矩形左上角的位置,x:100,y:30,单位像素 rect.resize(100, 40); // 矩形大小,宽100,高40,单位像素 rect.attr({ body: { fill: 'blue' // 填充色 }, label: { text: 'Hello', // 矩形上显示的文字 fill: 'white' // 文字颜色 } }); list.push(rect) rect.addTo(graph); // 将上面定义的矩形加入到画板中 alertText(rect); //自定义的改变元素显示内容的方法 var rect2 = rect.clone(); // 复制一个相同的矩形 rect2.translate(300, 0); // 将矩形在水平方向上向右移动300像素 rect2.attr('label/text', 'World!'); // 设置矩形2上的文字 rect2.addTo(graph); // 将矩形2加入到画板中 alertText(rect2) list.push(rect2); addArraw(rect, rect2) //将两个元素连接起来//创建一个椭圆var ellipse = new joint.shapes.standard.Ellipse(); ellipse.resize(100, 50); ellipse.position(100, 30); ellipse.attr('root/title', 'joint.shapes.standard.Ellipse'); ellipse.attr('label/text', 'Ellipse'); ellipse.attr('body/fill', 'lightblue'); ellipse.addTo(graph); ellipse.translate(200*(6-count), 0); ellipse.addTo(graph)//引入图片示例rect = new joint.shapes.standard.BorderedImage(); rect.resize(120, 100); rect.position(100, 30); rect.attr('root/title', 'joint.shapes.standard.BoarderedImage'); rect.attr('label/text', dataList[i].title+'\n'+dataList[i].name+'\n'+dataList[i].time); rect.attr('border/rx', 5); rect.attr('border/stroke','green') //设置边框颜色 rect.attr('border/stroke-width','3') rect.attr('image/xlinkHref', 'test.png');//元素中嵌入图片
rect = new joint.shapes.standard.EmbeddedImage(); rect.resize(120, 108); rect.position(125, 110); rect.attr('root/title', 'joint.shapes.standard.EmbeddedImage'); rect.attr('label/text', dataList[i].title+'\n'+dataList[i].name+'\n'+dataList[i].time); rect.attr('label/fill','white') rect.attr('label/refX','10px') rect.attr('image/xlinkHref', 'img/image.png'); rect.attr('image/x', '0px'); rect.attr('image/y', '0px'); rect.attr('image/refWidth', '100%'); rect.attr('image/refHeight', '100%'); rect.attr('image/preserveAspectRatio','xMidYMid'); //默认xMidYMin rect.addTo(graph);
4、连线
//定义连线的方法function addArraw(source, target) { link(source, target, "");}//定义连线 function link(source, target, label){ var cell = new joint.shapes.standard.Link({ source: { id: source.id }, target: { id: target.id }, labels: [{ position: 0.5, attrs: { text: { text: label || '', 'font-weight': 'bold', 'font-size': '12px' } } }], router: { name: 'manhattan' },//设置连线弯曲样式 'normal' - 普通线 'orthogonal' - 基础版的正交折线 'manhattan' - 优化版的正交折线 'metro' - 另一种正交折线 'oneSide' attrs: { '.connection': { stroke: '#C0D8FD',//连线颜色 }, '.marker-target': { fill: '#C0D8FD',//箭头颜色 d: 'M 20 -10 0 0 20 10 Z'//箭头样式 }, line: { stroke: '#4649F4', // SVG attribute and value 'stroke-width': 6,//连线粗细 // 'stroke-dasharray':"2 2" } } }); // if(list[2] == source){ // cell.set('vertices', [ // { x: 620, y: 55 },{ x: 625, y: 60 }, // ] // } //定义连线必须经过的点 // cell.attr('line/stroke-dasharray','5 2') //虚线,每隔5像素简单2像素 graph.addCell(cell); return cell;}
5、demo全
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title></title> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/2.2.1/joint.min.js"></script> </head> <body> <button onclick="addRectangle()">添加矩形</button> <button onclick="addEllipse()">添加椭圆</button> <span id="Tips"></span> <input id="alertText" style="display: none;" type="text" onchange="changeText()"/> <!-- 这里是画布 --> <div id="myGraph"></div> </body> <script type="text/jscript"> const graph = new joint.dia.Graph; // 创建画板,所有图上的元素都在画板里 var paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板 el: document.getElementById('myGraph'), // 指向HTML里ID为"myGraph"的元素 model: graph, // 指定画板 width: 1500, // 画布宽600像素 height: 400, // 画布高100像素 gridSize: 1, // 画布上元素拖动时步进的像素,默认1,设的高方便对齐 background: { // 画布背景色 color: 'rgba(0, 0, 0, 0.1)' }, interactive :true //图形元素是否可拖动 }); const list = []; let count; var obj; var code; // var code2 = $("input[id='alertText']").val(); document.getElementById("alertText").addEventListener("keydown", function(event) { if (event.keyCode==13){ obj.attr('label/text', code) } }); function changeText(){ code = $("#alertText").val(); obj.attr('label/text', code) } function addRectangle() { if(count > 1){ // 创建一个矩形 let rect = new joint.shapes.standard.Rectangle(); rect.position(100, 30); // 矩形左上角的位置,x:100,y:30,单位像素 rect.resize(100, 40); // 矩形大小,宽100,高40,单位像素 rect.attr({ body: { fill: 'red' // 填充色 }, label: { text: 'new', // 矩形上显示的文字 fill: 'white' // 文字颜色 } }); rect.translate(200*(6-count), 0); rect.addTo(graph) addArraw(list[list.length - 1], rect); alertText(rect) list.push(rect) count--; if(count==1){ addArraw(rect,list[0]); } } } function addEllipse() { if(count > 1){ var ellipse = new joint.shapes.standard.Ellipse(); ellipse.resize(100, 50); ellipse.position(100, 30); ellipse.attr('root/title', 'joint.shapes.standard.Ellipse'); ellipse.attr('label/text', 'Ellipse'); ellipse.attr('body/fill', 'lightblue'); ellipse.addTo(graph); ellipse.translate(200*(6-count), 0); ellipse.addTo(graph) addArraw(list[list.length - 1], ellipse); alertText(ellipse) list.push(ellipse) count--; if(count==1){ addArraw(ellipse,list[0]); } } } window.onload=function() { //模拟后端传入数据 var dataList = [{ title:"开立医嘱", name:"张三", time:"2020-12-18 00:00:00" },{ title:"药师审方", name:"李四", time:"2020-12-18 00:00:00" },{ title:"处方缴费", name:"王麻子", time:"2020-12-18 00:00:00" },{ title:"药房摆药", name:"康康", time:"2020-12-18 00:00:00" },{ title:"药房发药", name:"李雷", time:"2020-12-18 00:00:00" }] var data = [{ title:"开立医嘱", name:"张三", time:"2020-12-18 00:00:00" },{ title:"药师审方", name:"李四", time:"2020-12-18 00:00:00" },{ title:"处方缴费", name:"王麻子", time:"2020-12-18 00:00:00" }] let flag = true; //是否闭环 count = 5; // 循环创建 for(var i=0;i<dataList.length;i++){ var rect; rect = new joint.shapes.standard.EmbeddedImage(); rect.resize(180, 70); rect.position(80, 110); rect.attr('root/title', 'joint.shapes.standard.EmbeddedImage'); rect.attr('label/text', ' '+dataList[i].title+'\n' +' '+dataList[i].name+'\n' +dataList[i].time); rect.attr('label/fill','#3E3E3E') rect.attr('label/refX','1%') rect.attr('body/stroke','#2E78F6') rect.attr('image/xlinkHref', 'img/bg01.png'); rect.attr('image/x', '0px'); rect.attr('image/y', '0px'); rect.attr('image/refWidth', '100%'); rect.attr('image/refHeight', '100%'); rect.attr('image/preserveAspectRatio','xMidYMid'); //默认xMidYMin rect.addTo(graph); rect.translate(250*i, 0); for(var j=0;j<data.length;j++){ if(JSON.stringify(data[j])===JSON.stringify(dataList[i])){ rect.attr('image/xlinkHref', 'img/bg02.png'); rect.attr('label/fill','#2E78F6') } } rect.addTo(graph); if(i!=0 && i!=dataList.length){ addArraw(list[list.length - 1], rect); } if(i == dataList.length-2){ rect.translate(-250, 200); } if(flag&&i==dataList.length-1){ rect.translate(-1000, 200); addArraw(rect,list[0]) } alertText(rect); list.push(rect) } } function addArraw(source, target) { // 创建一条连线 link(source, target, ""); } //定义连线 function link(source, target, label){ var cell = new joint.shapes.standard.Link({ source: { id: source.id }, target: { id: target.id }, labels: [{ position: 0.5, attrs: { text: { text: label || '', 'font-weight': 'bold', 'font-size': '12px' } } }], router: { name: 'manhattan' },//设置连线弯曲样式 'normal' - 普通线 'orthogonal' - 基础版的正交折线 'manhattan' - 优化版的正交折线 'metro' - 另一种正交折线 'oneSide' attrs: { '.connection': { stroke: '#C0D8FD',//连线颜色 }, '.marker-target': { fill: '#C0D8FD',//箭头颜色 d: 'M 20 -10 0 0 20 10 Z'//箭头样式 }, line: { stroke: '#4649F4', // SVG attribute and value 'stroke-width': 6,//连线粗细 // 'stroke-dasharray':"2 2" } } }); graph.addCell(cell); return cell; } function alertText(rect){ paper.findViewByModel(rect).on('cell:pointerclick', function() { // var code = prompt("请输入我爱你:"); var elem = document.getElementById("alertText"); elem.setAttribute('style', 'display: block'); elem.setAttribute('style', 'position: absolute'); elem.setAttribute('style', 'left: 500px'); elem.setAttribute('style', 'top: 1000px'); elem.setAttribute('style', 'z-index: 10'); obj = rect; for(let i=0;i<list.length;i++){ if(list[i] == rect){ $("#Tips").html("请修改步骤"+(i+1)+"的值") $("#alertText").focus(); } } }); } </script></html>
6.结果展示

