赞
踩
JointJS是一个专注于图形可视化的js库,用于创建交互式的图形和图表。它基于HTML、SVG(可缩放矢量图形)和CSS(层叠样式表)技术,主要用于在Web应用程序中实现可视化图形编辑器或图表展示(流程图、关系图和定制化图形)。
由于jointJS是基于backbone( 实现了web前端MVC模式的js库 )的,因此有view(视图)和model(模型)的概念,使用jointJS需要引入jQuery、backbone、lodash以及jointJS的包,可以通过script标签引入,也可以通过npm安装。
中文文档:JointJS官方API的个人整理_graph.resetcells-CSDN博客
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>JointJS Sample</title>
- <meta charset="UTF-8">
- <link rel="stylesheet" type="text/css" href="css/joint.min.css" />
- <!-- 下面的顺序不能变 -->
- <script src="js/jquery.js"></script>
- <script src="js/lodash.js"></script>
- <script src="js/backbone.js"></script>
- <script src="js/joint.js"></script>
- <!-- 这是自己的JS文件 -->
- <script type="text/javascript" src="js/main.js"></script>
- </head>
- <body>
- <div id="canvas"></div>
- </body>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>JointJS Sample</title>
- <meta charset="UTF-8">
- <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/2.2.1/joint.min.css">
- <!--注意加载顺序-->
- <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>
npm install jointjs
以下是JointJS的一个简单示例代码:
- <template>
- <div class="canvas" ref="canvas"></div>
- </template>
- <script>
- import * as joint from 'jointjs'
- export default {
- data () {
- return {
- paper: null
- }
- },
- mounted() {
- this.init()
- },
- methods: {
- init() {
- // 创建了两个矩形元素和一个连接它们的图形
- let graph = new joint.dia.Graph // 创建画板,所有图上的元素都在画板里
- this.paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板
- el: this.$refs.canvas, // HTML上div ID document.getElementById('canvas'),
- model: graph, // 指定画板
- width: 600,
- height: 100,
- gridSize: 1 // 画布上元素拖动时步进的像素,默认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: 'blue' // 填充色
- },
- label: {
- text: 'Hello', // 矩形上显示的文字
- fill: 'white', // 文字颜色
- },
- })
- rect.addTo(graph) // 将上面定义的矩形加入到画板中
- let rect2 = rect.clone() // 复制一个相同的矩形
- rect2.translate(300, 0) // 将矩形在水平方向上向右移动300像素
- rect2.attr('label/text', 'World!') // 设置矩形2上的文字
- rect2.addTo(graph) // 将矩形2加入到画板中
- let link = new joint.shapes.standard.Link() // 创建一条连线
- link.source(rect) // 连线头为矩形1
- link.target(rect2) // 连线尾为矩形2
- link.addTo(graph) // 将上面定义的连线加入到画板中
- }
- }
- }
- </script>
- <style></style>
运行结果:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0,user-scalable=no" />
- <title>Title</title>
- <link rel="stylesheet" type="text/css" href="css/joint.css">
- <!--注意加载顺序-->
- <script src="js/jquery-3.2.1.min.js" type="text/javascript" charset="utf-8"></script>
- <script src="js/lodash.js"></script>
- <script src="js/backbone-min.js"></script>
- <script src="js/joint.min.js"></script>
- <style>
- #wrapper {
- margin: 10px auto;
- background: #fafafa;
- }
- </style>
- </head>
-
- <body>
- <!--准备一个容器存放画板-->
- <div id="wrapper"></div>
- </body>
- </html>
- var graph = new joint.dia.Graph; //生成画板
- var paper = new joint.dia.Paper({ //生成画布
- el: $('#wrapper'),
- width: 800,
- height: 500,
- model: graph,
- gridSize: 1
- });
- var cell = new joint.shapes.basic.Rect({
- position: {
- x: 100,
- y: 100
- },
- size: {
- width: 100,
- height: 100
- },
- attrs: {
- //边框,填充,边框宽度,样式
- rect: {
- fill: 'pink',
- 'stroke': 'black',
- 'stroke-width': 2,
- 'stroke-dasharray': 0
- },
- text: {
- //字体内容,颜色,粗细,大小
- text: '矩形',
- fill: 'black',
- 'font-weight': 'normal',
- 'font-size': 20
- }
- }
- });
- //把矩形放到画板中
- graph.addCells([cell]);
- cell.position(20, 20);
- console.log(cell.get('position')); //{x: 20, y: 20}
- cell.size(50, 50);
- console.log(cell.get('size')); //{width: 50, height: 50}
- cell.attr('text/text', '测试文本');
- cell.attr('text/fill', 'white');
- cell.attr('text/font-weight', 'bold');
- cell.attr('text/font-size', 14);
cell.attr('rect/stroke-dasharray', '2,5'); //虚线间隙
cell.translate(10, 10)
cell.resize(120, 120)
cell.rotate(45)
- cell.toFront()
- cell.toBack()
- cell.embed(cell1) //当移动cell时,cell1也会跟着移动
- cell.unembed(cell1) // 取消跟着移动
cell.clone()
cell.remove()
- paper.drawBackground({
- color: color
- });
paper.setDimensions(width, height);
转载自:用JointJS做一个简单的功能控制图 | 思诚之道
做一个简单的自动化功能控制图:
- <!DOCTYPE html>
- <html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>FBD Diagram</title>
- <meta name="description" content="功能控制图设计器" />
- <meta charset="UTF-8">
- <link rel="stylesheet" type="text/css" href="css/joint.min.css" />
- <script src="js/jquery.js"></script>
- <script src="js/lodash.js"></script>
- <script src="js/backbone.js"></script>
- <script src="js/joint.js"></script>
- <script src="js/main.js"></script>
- </head>
- <body>
- 将元素从左边拖入画布,双击删除
- <div id="myholder"></div>
- <button id="saveButton">保存</button>
- <button id="loadButton">加载</button> 图模型保存为JSon格式:
- <textarea id="mySavedModel" style="width:100%;height:300px">
- </textarea>
- </body>
- </html>
5.2 定义画板和画布
- var graph = new joint.dia.Graph; // 创建画板,所有图上的元素都在画板里
- var paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板
- el: $('#myholder'), // 指向HTML里ID为"myGraph"的元素
- model: graph, // 指定画板
- width: 600, // 画布宽600像素
- height: 300, // 画布高300像素
- gridSize: 5, // 画布上元素拖动时步进的为5像素,默认1
- drawGrid: true, // 显示步进点,方便对齐
- background: { // 画布背景色
- color: 'rgba(0, 0, 0, 0.1)'
- },
-
- // 连接线风格
- defaultLink: new joint.shapes.logic.Wire({
- connector: { name: 'jumpover' }, // 当两根连线交叉时,其中一根跳过
- }),
- linkPinning: false, // 连线必须连到某个元素,即不允许连到空白处
- snapLinks: {
- radius: 25 // 距离元素连接点25像素时自动连接上
- },
-
- // 验证连线是否允许,
- validateConnection: function(viewSource, magnetSource, viewTarget, magnetTarget, end, viewLink) {
- if (end === 'target') {
- // 连线目标必须时一个"in"类型连接点
- if (!magnetTarget || !magnetTarget.getAttribute('port-group') || magnetTarget.getAttribute('port-group').indexOf('in') < 0) {
- return false;
- }
-
- // 检查连接点是否已经有连线接入,不允许多重接入
- var portUsed = this.model.getLinks().some(function(link) {
- return (link.id !== viewLink.model.id &&
- link.get('target').id === viewTarget.model.id &&
- link.get('target').port === magnetTarget.getAttribute('port'));
- });
- return !portUsed;
-
- } else { // end === 'source'
- // 连线起始点必须时一个"out"类型连接点
- return magnetSource && magnetSource.getAttribute('port-group') && magnetSource.getAttribute('port-group').indexOf('out') >= 0;
- }
- },
- });
上面的属性有些多,但大部分都好理解,主要是验证函数validateConnection()
,其返回true
或false
,用来决定连线是否被允许。传入的参数viewSource
和viewTarget
分别是画线过程中鼠标按钮释放时,当前连线头和尾的元素;而参数magnetSource
和magnetTarget
分别是当前连线头和尾的”连接点”(port)。”连接点”的概念下面会讲到。
5.3 定义基本图例元素
这里,我们创建了与、或、非,三个基本元素,因为是图例元素,所以不允许连入连出。我们使用标准库提供的”joint.shapes.devs.Model”元素,因为它很方便设置”连接点”(port)。
- // 创建基础元件模板
- var gateTemplate = new joint.shapes.devs.Model({
- position: { // 默认位置
- x: 0,
- y: 0
- },
- size: { // 默认大小
- width: 50,
- height: 60
- },
- // "连接点"(port)的风格
- portMarkup: '<rect class="joint-port-body" width="10" height="3" style="fill:black" />',
- // "连接点"(port)标签文字的显示风格
- portLabelMarkup: '<text class="port-label joint-port-label" font-size="10" y="0" fill="#000" /> ',
- ports: { // 定义连接点
- groups: {
- 'in': { // "入"连接点的属性和风格
- attrs: {
- '.port-body': { // 这是JointJS类库预定义的连接点属性类
- magnet: 'passive', // 该连接点只入不出
- },
- '.joint-port-body': { // 这是JointJS类库预定义的连接点风格类
- x:-10 // "入"连接点左移10个像素,这样可以显示在元素外部
- }
- },
- label: {
- position: {
- args: {x:18}, // 标签右移,这样可以显示在元素内部
- },
- },
- },
- 'out': {
- label: { // "出"连接点的属性和风格
- position: {
- args: {x:-23}, // 标签左移,这样可以显示在元素内部
- },
- },
- }
- }
- },
- attrs: {
- '.label': {
- 'type': 'primary', // 自定义一个图例属性,后面事件操作时判断用
- fontSize: 12, // 标签字体
- 'ref-x': .5, // 标签相对于元素的水平位置
- 'ref-y': .05 // 标签相对于元素的垂直位置
- },
- }
- });
-
- // 生成"与"元素,两个"入"连接点,一个"出"连接点,显示"And"字样标签
- function genAndPr() {
- return gateTemplate.clone().set('inPorts', ['IN1', 'IN2']).set('outPorts', ['OUT']).attr('.label/text', 'And');
- }
-
- // 生成"或"元素,两个"入"连接点,一个"出"连接点,显示"Or"字样标签
- function genOrPr() {
- return gateTemplate.clone().set('inPorts', ['IN1', 'IN2']).set('outPorts', ['OUT']).attr('.label/text', 'Or');
- }
-
- // 生成"非"元素,一个"入"连接点,一个"出"连接点,显示"Not"字样标签
- function genNotPr() {
- return gateTemplate.clone().set('inPorts', ['IN ']).set('outPorts', ['OUT']).attr('.label/text', 'Not');;
- }
-
- // 图例加入到画板左侧
- graph.addCell(genAndPr().translate(20, 20));
- graph.addCell(genOrPr().translate(20, 120));
- graph.addCell(genNotPr().translate(20, 220));
-
- // 添加一个分割栏将图例和绘图区域分开
- var separator = new joint.shapes.standard.Polyline();
- separator.resize(5, 400);
- separator.position(95, 0);
- separator.addTo(graph);
上面关键的概念就是”连接点”(port),JointJS库中joint.shapes.devs.Model
元素默认支持”入”(in)和”出”(out)两种连接点,分别显示在元素图形的左边和右边,多个连接点会自动排列。通过前一段代码中paper
的linkPinning
属性设置,可以要求”连线”只允许接在”连接点”上。上面我们定义了”连接点”风格为一个长10px
宽3px
的水平线。上例中,因为我重写了”连接点”的标记portMarkup
,去掉了允许连线的属性,所以这些连接点目前都无法被连线。
5.4 定义绘图元素
- function genAnd() {
- return genAndPr().set('portMarkup', '<rect class="port-body joint-port-body" width="10" height="2" style="fill:black" />').attr('.label/type', 'instance');
- }
- function genOr() {
- return genOrPr().set('portMarkup', '<rect class="port-body joint-port-body" width="10" height="2" style="fill:black" />').attr('.label/type', 'instance');
- }
- function genNot() {
- return genNotPr().set('portMarkup', '<rect class="port-body joint-port-body" width="10" height="2" style="fill:black" />').attr('.label/type', 'instance');
- }
将图例元素中的portMarkup
改了,其实就是增加了css类port-body
,这是JointJS库中预定义的。该类中设置了”元素”的magnet
属性,设为true
时可入可出;passive
时只入不出;false
时不能连线。这样,我们的绘图元素就可以接上连线了。另外,这里将自定义属性.label/type
改为instance
主要是后面的事件判断用。
5.5 定义鼠标事件,来支持将图例元素拖入绘图区域
- paper.on({ // JointJS事件都定义在画布上
- // 当鼠标左键按下时
- 'element:pointerdown': function(elementView, evt) {
- // 当图例元素被拖走时,在原来的位置创建一个新的图例元素
- if (elementView.model.attr('.label/type') == 'primary') {
- var type = elementView.model.attr('.label/text');
- if (type == 'And') {
- graph.addCell(genAndPr().translate(20, 20));
- } else if (type == 'Or') {
- graph.addCell(genOrPr().translate(20, 120));
- } else if (type == 'Not') {
- graph.addCell(genNotPr().translate(20, 220));
- }
- // 被拖动的元素挪到图层的最上层,这样可以遮盖现有元素
- elementView.model.toFront();
- } else if (elementView.model.attr('.label/type') == 'instance') {
- // 对于绘图元素,记住其被拖动时的起始点,当拖动位置超出绘图区域时,可以回到原点
- evt.data = elementView.model.position();
- }
- },
-
- // 当鼠标左键抬起时
- 'element:pointerup': function(elementView, evt, x, y) {
- if (elementView.model.attr('.label/type') == 'primary') {
- // 对于图例元素,当其被拖入绘图区域时,则在该位置创建一个新的绘图元素,并删除被拖动的图例元素
- if (elementView.model.position().x > 105) {
- var type = elementView.model.attr('.label/text');
- if (type == 'And') {
- graph.addCell(genAnd().translate(elementView.model.position().x, elementView.model.position().y));
- } else if (type == 'Or') {
- graph.addCell(genOr().translate(elementView.model.position().x, elementView.model.position().y));
- } else if (type == 'Not') {
- graph.addCell(genNot().translate(elementView.model.position().x, elementView.model.position().y));
- }
- }
- // 删除当前被拖动的元素
- graph.removeCells(elementView.model);
- } else {
- // 对于绘图元素,当其被拖出绘图区域时,则将其移回原点
- if (elementView.model.position().x < 110) {
- elementView.model.position(evt.data.x, evt.data.y);
- }
- }
- },
-
- // 当鼠标左键双击时
- 'element:pointerdblclick': function(elementView, evt) {
- // 双击绘图元素则删除该元素,相应的连线也会被自动删除
- if (elementView.model.attr('.label/type') == 'instance') {
- elementView.model.remove();
- }
- },
- })
JointJS的事件都定义在画布paper上,可以参考这里的说明事件的种类很多,可以在”元素”、”连线”或”空白处”上监听,可以是各种鼠标事件,这里不赘述了。大部分事件都接受4个参数:
上例中的事件函数,定义了将图例元素拖入绘图区域,并创建一个新的绘图元素的过程。
保存上面的代码并在浏览器里打开,大家应该可以看到如下内容。
JointJS源码托管在Github中。更详细的开发API文档可以在官方API文档中找到。 本文中的示例代码,可以在这里下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。