JointJs快速入门

JointJs快速入门JointJS 速览入门 HelloWorld 下面时官方给出的入门 demo 例子 可以看到 JointJS 依赖 jquery lodash backbone 使用 JointJS 只需要引入 joint css 和 joint js 即可 定义一个 div 用来盛放绘制的图形 DOCTYPE tml html head head html

JointJS速览

入门
Hello World
下面时官方给出的入门demo例子,可以看到 JointJS 依赖jquery、lodash、backbone,使用 JointJS 只需要引入joint.css和joint.js即可。定义一个div用来盛放绘制的图形。




 
   
    
   

代码解析
在上面的js代码中,有以下几类对象:

paper:JointJS中最外层的对象,用来连接内外:内部装载的是哪个graph,外部是渲染在哪个div中。gridSize属性是用来指明元素对齐的网格的大小。影响元素移动的粒度。paper在渲染过后,可以使用paper.scale()去实现缩放,使用paper.translate()去实现整体位移。paper的完整属性可以参看这里。

graph:用来承载各个元素的画布。

Rectangle:矩形元素。一个元素可以是通过构造器,如new joint.shapes.standard.Rectangle(),来实例化,也可以通过clone方法去得到。shapes.standard下提供了十几种常见的图形元素,比如圆形、椭圆、带标题的矩形等等。官网提到,可以通过继承Element对象,来自定义自己的元素。Element最常用的几个方法如下:

element.position() – 设置元素原点(左上角)相对于paper坐标系的位置(考虑paper缩放和其他变换)。
element.resize() – 设置元素的尺寸。
element.clone() – 克隆现有元素,包括其位置,尺寸和属性。
element.translate() – 沿两个坐标轴移动元素指定的距离。还有缩放和旋转元素的方法。
element.addTo() – 将元素添加到graph中以便可以呈现它。
每个元素都可以通过element.attr方法来重写属性。这个元素支持哪些属性,可以通过joint.js中查看代码来得知,比如Rectangle元素在joint.js中的定义代码如下:










Element.define('standard.Rectangle', { attrs: { body: { refWidth: '100%', refHeight: '100%', strokeWidth: 2, stroke: '#000000', fill: '#FFFFFF' }, label: { textVerticalAnchor: 'middle', textAnchor: 'middle', refX: '50%', refY: '50%', fontSize: 14, fill: '#' } } }, { markup: [{ tagName: 'rect', selector: 'body', }, { tagName: 'text', selector: 'label' }] }); 

改变Element的属性在上例html中演示了两种写法rect,rect2,个人感觉第二种更直观。

Link:线条元素。指明线条的两个端点即实现了连线。Link和普通的元素没有很大区别。

中级

特殊属性
相对维度
使用SVG时最常见的请求之一是相对设置SVGElements的维度。 JointJS允许您使用一组ref属性来实现。这些属性允许您将子元素的大小调整为形状模型的尺寸的百分比。此外,由于所有计算都是程序化的,并且不依赖于浏览器的bbox测量,因此使用这些属性不会影响应用程序的性能。




Link箭头
可以通过SourceMarker和TargetMarker为Link自定义箭头,如下面的示例代码为其定义了一个矩形箭头和圆形箭尾:、

link.attr({ line: { sourceMarker: { 'type': 'rect', 'width': 50, 'height': 10, 'y': -5, 'fill': 'rgba(255,0,0,0.3)', 'stroke': 'black' }, targetMarker: { 'type': 'circle', 'r': 10, 'cx': 10, 'fill': 'rgba(0,255,0,0.3)', 'stroke': 'black' } } }); 

事件

paper的内建事件
Paper提供了常见的事件捕捉,如单元格/元素/线条/空白处被点击,鼠标移动,线条连接元素,线条取消连接元素等等,详细的可参见。事件的回调写法如下:

paper.on('blank:pointerdown', function(evt, x, y) { alert('pointerdown on a blank area in the paper.') }) 

连续的一组事件,可以共享数据,写法如下:

// Create a new link by dragging paper.on({ 'blank:pointerdown': function(evt, x, y) { var link = new joint.dia.Link(); link.set('source', { x: x, y: y }); link.set('target', { x: x, y: y }); link.addTo(this.model); evt.data = { link: link, x: x, y: y }; }, 'blank:pointermove': function(evt, x, y) { evt.data.link.set('target', { x: x, y: y }); }, 'blank:pointerup': function(evt) { var target = evt.data.link.get('target'); if (evt.data.x === target.x && evt.data.y === target.y) { // remove zero-length links evt.data.link.remove(); } } }); 

graph的内建事件
graph提供了一些捕捉element位置变化、大小变化的事情,如change:position是监测element的位置发生了变化,change:target是监测link的指向发生了变化,示例代码如下:

graph.on('change:position', function(cell) { var center = cell.getBBox().center(); var label = center.toString(); cell.attr('label/text', label); }); graph.on('change:target', function(cell) { var target = new g.Point(cell.target()); var label = target.toString(); cell.label(0, { attrs: { label: { text: label } } }); }); 

子元素监听事件
在下面的代码中定义了一个新的元素,他由一个矩形+一个按钮组成。现在我们想监听按钮被点击的事情,该怎么处理?注意97行代码,我们需要通过子元素的event属性,告知上层event是需要监听的。然后通过paper.on来捕捉,并定义响应。

 
   
    
   

自定义视图事件
对于更高级的事件自定义,我们可以通过重写paper的默认view来实现,下面是示例代码。在下面的例子中,我们禁用了默认的视图,通过elementview和linkview重写了事件。新的效果是,当某个元素或者线条被双击时会删除它本身。

var paper = new joint.dia.Paper({ el: document.getElementById('paper-custom-view-events'), model: graph, width: 600, height: 100, gridSize: 1, background: { color: 'white' }, interactive: false, // disable default interaction (e.g. dragging) elementView: joint.dia.ElementView.extend({ pointerdblclick: function(evt, x, y) { this.model.remove(); } }), linkView: joint.dia.LinkView.extend({ pointerdblclick: function(evt, x, y) { this.model.remove(); } }) }); 

自定义元素
可以先看一下源码中是如何定义一个Rectangle Element的,代码如下:

joint.dia.Element.define('standard.Rectangle', { attrs: { body: { refWidth: '100%', refHeight: '100%', strokeWidth: 2, stroke: '#000000', fill: '#FFFFFF' }, label: { textVerticalAnchor: 'middle', textAnchor: 'middle', refX: '50%', refY: '50%', fontSize: 14, fill: '#' } } }, { markup: [{ tagName: 'rect', selector: 'body', }, { tagName: 'text', selector: 'label' }] }); 
  • 通过Element.define函数去声明这是一个Element的定义。
  • standard.Rectangle是所定义Element的名称,应是唯一的。这里隐藏了joint.shapes,所以实际上的全名是joint.shapes.standard.Rectangle。
  • markup是定义子元素的地方。tagName指明了子元素的名字rect->SVGRectElement,text->SVGTextElement。selector给出了两个子元素的在本元素类的名称。可以看出joint.js最底层是svg对象。
  • attrs设置默认属性。使用在markup中定义的selector标识符,为各个子元素指定属性。可以看到这里子元素的大小都是通过相对维度去设定的。

这里还没有提到的是构造函数。在上面的元素定义中,还可以增加一个构造函数段,如下:

{ createRandom: function() { var rectangle = new this(); var fill = '#' + ('000000' + Math.floor(Math.random() * ).toString(16)).slice(-6); var stroke = '#' + ('000000' + Math.floor(Math.random() * ).toString(16)).slice(-6); var strokeWidth = Math.floor(Math.random() * 6); var strokeDasharray = Math.floor(Math.random() * 6) + ' ' + Math.floor(Math.random() * 6); var radius = Math.floor(Math.random() * 21); rectangle.attr({ body: { fill: fill, stroke: stroke, strokeWidth: strokeWidth, strokeDasharray: strokeDasharray, rx: radius, ry: radius }, label: { // ensure visibility on dark backgrounds fill: 'black', stroke: 'white', strokeWidth: 1, fontWeight: 'bold' } }); return rectangle; } 

这样,使用的时候,就可以通过joint.shapes.standard.Rectangle.createRandom();来调用构造函数去创建元素对象。

自定义属性
自定义属性需要在使用时通过以下方法进行:

var rect3 = new joint.shapes.lefer.Rectangle() rect3.attr('bussiness/title', 'lefer'); 

需要注意的是,自定义的业务属性需要在自定义元素时没有出现过才能成功。

使用port
前面的示例中是在渲染时就指明了图形的连接关系,还有一种很常见的情况是由用户来拖拽连线。这个时候需要用到joint.shapes.devs.js。下面是示例代码

WORKING WITH PORTS Many diagramming applications deal with elements with ports. Ports are usually displayed as circles inside diagram elements and are used not only as "sticky" points for connected links but they also further structure the linking information. It is common that certain elements have lists of input and output ports. A link might then point not to the element as a whole but to a certain port instead. JointJS has a built-in support for elements with ports, linking between ports and a facility for defining what connections are allowed and what not. This is useful if you, for example, want to restrict linking in between input ports, or output ports or between a certain port of an element A and a certain port of an element B. This tutorial shows you how you can do all that. Creating elements with ports The easiest way to start with elements with ports is using the joint.shapes.devs plugin. Search for joint.shapes.devs.js file. This plugin defines one important shape, the joint.shapes.devs.Model*. You can just instantiate that shape and pass the inPorts and outPorts arrays as parameters. You can further set the coloring of the ports and label for your element as you can see in the example below. Moreover, JointJS takes care of preparing the view and the magnets for UI interaction. That's why you can already click and drag a port and JointJS automatically creates a link coming out of that port. JointJS and the joint.shapes.devs.Model also makes it easy to change ports. Simply set the inPorts/outPorts arrays of your element: element.set('inPorts', ['newIn1', 'newIn2', 'newIn3']); element.set('outPorts', ['newOut1', 'newOut2']); *DEVS is an abbreviation for Discrete EVent System specification and is a formalism for modeling and analyzing general systems. This formalism uses two types of models (Atomic and Coupled) both having a set of input and output ports. Magnets in JointJS are SVG sub-elements that serve as sticky points for links. If you use the joint.shapes.devs plugin, you don't have to define your magnets yourself, instead the joint.shapes.devs.Model shape does it for you. (function() { var graph = new joint.dia.Graph; var paper = new joint.dia.Paper({ el: $('#paper-create'), width: 650, height: 200, gridSize: 1, model: graph }); var m1 = new joint.shapes.devs.Model({ position: { x: 50, y: 50 }, size: { width: 90, height: 90 }, inPorts: ['in1','in2'], outPorts: ['out'], ports: { groups: { 'in': { attrs: { '.port-body': { fill: '#16A085' } } }, 'out': { attrs: { '.port-body': { fill: '#E74C3C' } } } } }, attrs: { '.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 }, rect: { fill: '#2ECC71' } } }); graph.addCell(m1); }()); Linking elements with ports Now when you have your elements with ports created, you can start observing what port is connected with a link to what other port. This is easy to do thanks to JointJS storing the information about ports in the link models themselves once the links are created via the UI. The following example shows you how you can get the linking information. Try to connect a port of one element to another port of another element. (function() { var graph = new joint.dia.Graph; var paper = new joint.dia.Paper({ el: $('#paper-link'), width: 650, height: 200, gridSize: 1, model: graph }); var m1 = new joint.shapes.devs.Model({ position: { x: 50, y: 50 }, size: { width: 90, height: 90 }, inPorts: ['in1','in2'], outPorts: ['out'], ports: { groups: { 'in': { attrs: { '.port-body': { fill: '#16A085' } } }, 'out': { attrs: { '.port-body': { fill: '#E74C3C' } } } } }, attrs: { '.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 }, rect: { fill: '#2ECC71' } } }); graph.addCell(m1); var m2 = m1.clone().translate(300, 0).attr('.label/text', 'Model 2'); graph.addCell(m2); graph.on('change:source change:target', function(link) { var sourcePort = link.get('source').port; var sourceId = link.get('source').id; var targetPort = link.get('target').port; var targetId = link.get('target').id; var m = [ 'The port ' + sourcePort, ' of element with ID ' + sourceId, ' is connected to port ' + targetPort, ' of elemnt with ID ' + targetId + ''
    ].join('');
    
    out(m);
});
function out(m) {
    $('#paper-link-out').html(m);
}
}());
Linking restrictions
Now you know how to create elements with ports and how to get the linking information. Another practical functionality related to elements with ports and their links is restricting certain connections. Say you want links to never start in input ports and never end in output ports. This is the most usual case. However, all kinds of restrictions are possible and application specific. JointJS doesn't limit you. Instead, it allows you to define a function that simply returns true if a connection between a source magnet of a source element and a target magnet of a target element is allowed, and false otherwise. If the connection is not allowed JointJS does not connect the magnets (and associated ports). Furthermore, you can mark certain magnets as "passive" in which case JointJS treats these magnets in a way that they can never become a source of a link. For further information, please see the list of options that you can pass to the joint.dia.Paper in the API reference page, especially the two related functions: validateConnection() and validateMagnet().
 (function() {
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
    el: $('#paper-restrict'),
    width: 650, height: 200, gridSize: 1,
    model: graph,
    defaultLink: new joint.dia.Link({
        attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } }
    }),
    validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
        // Prevent linking from input ports.
        if (magnetS && magnetS.getAttribute('port-group') === 'in') return false;
        // Prevent linking from output ports to input ports within one element.
        if (cellViewS === cellViewT) return false;
        // Prevent linking to input ports.
        return magnetT && magnetT.getAttribute('port-group') === 'in';
    },
    validateMagnet: function(cellView, magnet) {
        // Note that this is the default behaviour. Just showing it here for reference.
        // Disable linking interaction for magnets marked as passive (see below `.inPorts circle`).
        return magnet.getAttribute('magnet') !== 'passive';
    }
});
var m1 = new joint.shapes.devs.Model({
    position: { x: 50, y: 50 },
    size: { width: 90, height: 90 },
    inPorts: ['in1','in2'],
    outPorts: ['out'],
    ports: {
        groups: {
            'in': {
                attrs: {
                    '.port-body': {
                        fill: '#16A085',
                        magnet: 'passive'
                    }
                }
            },
            'out': {
                attrs: {
                    '.port-body': {
                        fill: '#E74C3C'
                    }
                }
            }
        }
    },
    attrs: {
        '.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 },
        rect: { fill: '#2ECC71' }
    }
});
graph.addCell(m1);
var m2 = m1.clone();
m2.translate(300, 0);
graph.addCell(m2);
m2.attr('.label/text', 'Model 2');
}());
Link snapping
To improve user experience little bit you might want to enable the link snapping. While the user is dragging a link, it searches for the closest port in the given radius. Once a suitable port is found (it meets requirements specified in validateConnection()) the link automatically connects to it. You can try this functionality in the example below.
 (function() {
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
    el: $('#paper-link-snapping'),
    width: 650, height: 200, gridSize: 1,
    model: graph,
    defaultLink: new joint.dia.Link({
        attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } }
    }),
    validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
        // Prevent loop linking
        return (magnetS !== magnetT);
    },
    // Enable link snapping within 75px lookup radius
    snapLinks: { radius: 75 }
});
var m1 = new joint.shapes.devs.Model({
    position: { x: 50, y: 50 },
    size: { width: 90, height: 90 },
    inPorts: ['in1','in2'],
    outPorts: ['out'],
    ports: {
        groups: {
            'in': {
                attrs: {
                    '.port-body': {
                        fill: '#16A085',
                        magnet: 'passive'
                    }
                }
            },
            'out': {
                attrs: {
                    '.port-body': {
                        fill: '#E74C3C'
                    }
                }
            }
        }
    },
    attrs: {
        '.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 },
        rect: { fill: '#2ECC71' }
    }
});
graph.addCell(m1);
var m2 = m1.clone();
m2.translate(300, 0);
graph.addCell(m2);
m2.attr('.label/text', 'Model 2');
})();
Marking available magnets
Another way how to make user's life easier can be to offer him all magnets he can connect to while he is dragging a link. To achieve this you have to enable markAvailable option on the paper and add some css rules into your stylesheet like in the example bellow.
 /* port styling */
.available-magnet {
    fill: yellow;
}
/* element styling */
.available-cell rect {
    stroke-dasharray: 5, 2;
}
(function() {
    var graph = new joint.dia.Graph;
    var paper = new joint.dia.Paper({
        el: $('#paper-mark-available'),
        width: 650, height: 200, gridSize: 1,
        model: graph,
        defaultLink: new joint.dia.Link({
            attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } }
        }),
        validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
            // Prevent linking from input ports.
            if (magnetS && magnetS.getAttribute('port-group') === 'in') return false;
            // Prevent linking from output ports to input ports within one element.
            if (cellViewS === cellViewT) return false;
            // Prevent linking to input ports.
            return magnetT && magnetT.getAttribute('port-group') === 'in';
        },
        // Enable marking available cells & magnets
        markAvailable: true
    });
    var m1 = new joint.shapes.devs.Model({
        position: { x: 50, y: 50 },
        size: { width: 90, height: 90 },
        inPorts: ['in1','in2'],
        outPorts: ['out'],
        ports: {
            groups: {
                'in': {
                    attrs: {
                        '.port-body': {
                            fill: '#16A085',
                            magnet: 'passive'
                        }
                    }
                },
                'out': {
                    attrs: {
                        '.port-body': {
                            fill: '#E74C3C'
                        }
                    }
                }
            }
        },
        attrs: {
            '.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 },
            rect: { fill: '#2ECC71' }
        }
    }).addTo(graph);
    var m2 = m1.clone().translate(300, 0).attr('.label/text', 'Model 2').addTo(graph);
})();

文章转自;https://www.lefer.cn/posts/8954/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199925.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午11:54
下一篇 2026年3月20日 上午11:54


相关推荐

  • webpack相关配置

    webpack相关配置文章目录 webpack 的概念 webpack 的基本使用项目目录并初始化创建首页及 js 文件以 jQuery 为例安装 jQuery 导入 jQuery 安装 webpack webpack 的相关设置设置 webpack 的打包入口 出口设置 webpack 的自动打包配置 html webpack pluginwebpac 中的加载器 使用 webpack 打包发布项目 在 webpack 中使用 vueVue 单文件组件在 webpack 中使用 vue 写在最后 webpack 的概念 we

    2026年3月19日
    2
  • mysql 隐式类型转换_date_range函数

    mysql 隐式类型转换_date_range函数一、如果表定义的是varchar字段,传入的是数字,则会发生隐式转换。1、表DDL2、传int的sql3、传字符串的sql仔细看下表结构,rid的字段类型:而用户传入的是int,这里会有一个隐式转换的问题,隐式转换会导致全表扫描。把输入改成字符串类型,执行计划如下,这样就会很快了。此外,还需要注意的是:数字类型的0001等价于1字符串的0001和1不等价二、如果表定义的是int字段,传入的是字符串…

    2022年10月11日
    4
  • 服务器bios界面usb无线网卡,BIOS怎么开启无线网卡

    服务器bios界面usb无线网卡,BIOS怎么开启无线网卡

    2021年8月16日
    66
  • [Pytorch系列-64]:生成对抗网络GAN – 图像生成开源项目pytorch-CycleGAN-and-pix2pix : 有监督图像生成pix2pix的基本原理[通俗易懂]

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客本文网址:第1章关键参考信息1.1项目详细论文:Image-to-ImageTranslationwithConditionalAdversarialNetworks论文链接:https://arxiv.org/abs/1611.07004代码链接:https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix1.2GAN工作原

    2022年4月11日
    102
  • Java之单例模式

    Java之单例模式

    2021年7月21日
    56
  • 浅谈可重入函数与不可重入函数

    浅谈可重入函数与不可重入函数在实时系统的设计中 经常会出现多个任务调用同一个函数的情况 如果有一个函数不幸被设计成为这样 那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据 从而导致不可预料的后果 这样的函数是不安全的函数 也叫不可重入函数 相反 肯定有一个安全的函数 这个安全的函数又叫可重入函数 那么什么是可重入函数呢 所谓可重入是指一个可以被多个任务调用的过程 任务在调用时不必担心数据是否会出错 一个可

    2026年3月16日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号