关于端口,我也不知道怎么解释,就用joint官网的这句话先打个头。

Many diagramming applications deal with the idea of elements with ports. Ports are often displayed as circles inside diagram elements.

They are not only used 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 not then point to an element as a whole, but to a certain port instead.

许多图表应用程序处理带有端口的元素的概念。端口通常在图表元素中显示为圆形。

它们不仅被用作连接链接的“粘性”点,还可以进一步构造链接信息。

某些元素通常有输入和输出端口列表。链接可能不会指向整个元素,而是指向某个端口。

大概意思就是,例如一个源节点连接了多个目标元素,此时他们之间的连线是从源节点的任意位置连接到目标元素的任意位置的,例如这样:

可以看到,从node2出发的线围绕着node2的边缘,连接到的node3/4/5/6箭头也是在节点的边缘;

如果我们有了端口,将端口设置在节点的某些固定位置,通过一定的配置,实现连线是从node2的端口指向node3/4/5/6的端口,例如:

看见了吗兄弟们er。对就是这个意思!!

joint中是内置有一个Model类型的,joint.shapes.devs.Model,包含了端口,可以直接使用,但是我觉得太丑啦,默认长这样:

当时想直接基于Model把样式成自己需要的样子,忘记因为什么原因放弃了,不过结果应该是没弄好,菜是原罪。。。

后来扒拉了下jointjs的源码,根据Model的定义,给自定义的shape增加了渲染端口的代码,成了成了!

所以这篇文章是在自定义shape的基础上增加了自定义端口。自定义shape在自定义shape

如下是实现方式:

  • 首先是shape.js中在自定义shape基础上增加对于port样式结构的定义:

    let customShape = joint.shapes.basic.Generic.extend({
    markup: `<g class="rotatable">
    <rect class="body" />
    <text class="text"></text>
    </g>`,
    /** 在这里进行对port结构的定义,在这里定义成了圆形 */
    portMarkup: `<g class="port-body">
    <circle></circle>
    </g>`,
    /** option的定义见下方 */
    defaults: joint.util.defaultsDeep(option, joint.shapes.basic.Generic.prototype.defaults),
    initialize: function() {
    this.on(
    'change:label',
    function() {
    this.updateRectangles();
    },
    this
    );
    this.updateRectangles();
    joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments);
    /** 初始化里添加关于端口属性改变的监听以及方法调用 */
    this.on('change:inPorts change:outPorts', this.updatePortItems, this);
    this.updatePortItems();
    },
    updateRectangles() {
    const attrs = this.get('attrs');
    const rect = { label: this.get('label') };
    attrs['.text'].text = rect.label;
    },
    /** 端口的相关函数 */
    updatePortItems(model, changed, opt) {
    /** 确保每个端口都是唯一的 */
    const inPorts = joint.util.uniq(this.get('inPorts'));
    const outPorts = joint.util.difference(joint.util.uniq(this.get('outPorts')), inPorts); const inPortItems = this.createPortItems('in', inPorts);
    const outPortItems = this.createPortItems('out', outPorts); this.prop('ports/items', inPortItems.concat(outPortItems), joint.util.assign({ rewrite: true }, opt));
    }, createPortItem(group, port) {
    return {
    id: port,
    group
    };
    }, createPortItems(group, ports) {
    return joint.util.toArray(ports).map(this.createPortItem.bind(this, group));
    }
    });
  • 其次是option的配置,主要是port的样式:

    let option = {
    type: 'basic.customShape',
    size: {
    width: 100,
    height: 50
    },
    attrs: {
    '.body': {
    width: 100,
    height: 50,
    fill: '#ddd'
    },
    '.text': {
    fill: '#000',
    textVerticalAnchor: 'middle',
    textAnchor: 'middle',
    refX: '50%',
    refY: '50%'
    }
    },
    /** 对于端口的样式配置 */
    ports: {
    groups: {
    in: {
    /** 端口相对于元素的位置 */
    position: {
    name: 'left'
    },
    /** 端口样式 */
    attrs: {
    '.port-body circle': {
    r: 8,
    fill: '#fd6d42'
    }
    }
    },
    out: {
    position: {
    name: 'right'
    },
    attrs: {
    '.port-body circle': {
    r: 8,
    fill: '#3371FC'
    }
    }
    }
    }
    },
    };
  • 做完这些你会发现页面上并没有出现左右两个port,还需要在定义节点的时候,增加你对与端口的传值:

    /** 创建节点 */
    createNode(){
    this.nodes.forEach(ele => {
    let node = new customShape({
    id: ele.id,
    size: {
    width: 100,
    height: 50
    },
    /** 增加你需要的端口,端口是数组,可以传多个值,位置会默认均匀分布在你所规定的边上 */
    inPorts: ['in'],
    outPorts: ['out'],
    attrs: {
    text: {
    text: ele.label
    }
    }
    });
    this.nodeList.push(node);
    })
    this.graph.addCell(this.nodeList);
    }
  • 搞完这个你发现还是不对,这线还是没有跟端口连在一起啊。。。创建连线时,需要指定连线的位置,如下:

    /** 创建连线 */
    createLink(){
    this.links.forEach(ele => {
    let link = new joint.shapes.standard.Link({
    source: {
    id: ele.from,
    /** 从out端口连出线 */
    port: ['out']
    },
    target: {
    id: ele.to,
    /** 从in端口入线 */
    port: ['in']
    },
    attrs: {
    line: {
    stroke: '#aaa',
    strokeWidth: 1
    }
    }
    });
    this.linkList.push(link);
    })
    this.graph.addCell(this.linkList);
    }

此时就实现了第二张图的效果了,端口的样式是完全可以自定义的,需要其他形状,只需要通过svg绘制对应的元素就可以了,如果不需要也可以设置大小给隐藏掉,使得整体更美观。

例如,我可以将圆形端口的半径设置为0,将端口隐藏:

over~

[jointjs] 端口(port)的更多相关文章

  1. 邮件服务端口 port 25、109、110、143、465、995、993

    邮件服务端口 port 25.109.110.143.465.995.993   25端口(SMTP):25端口为SMTP(Simple Mail TransferProtocol,简单邮件传输协议) ...

  2. 查看linux中某个端口port是否被利用

    (1)lsof -i:端口号查看某个端口是否被占用 (2)netstat -an|grep 80 netstat -- show network status (3)杀掉进程 kill pid 注意: ...

  3. [No0000CB]如何在命令行(cmd)通过TCP/IP端口(port)查询所在的进程号(pid)或进程名称,并终止该进程

      1)首先查找占用某个端口的进程PID netstat -ano | findstr [port] 2)根据该进程pid查询进程名称或标题,确认那个程序在占用该端口 tasklist /v | fi ...

  4. Linux Tomcat 80端口 Port 80 required by Tomcat v8.5 Server at localhost is already in use.

    Port 80 required by Tomcat v8.5 Server at localhost is already in use. The server may already be run ...

  5. [Erlang危机](5.1.4)端口port

      原创文章,转载请注明出处:server非业余研究http://blog.csdn.net/erlib 作者Sunface 或port drivers15.   全程跟踪端口数会对诊断负载或进程泄漏 ...

  6. Docker入门系列7 动态映射端口port mapping

    为何想要动态映射端口呢? 因为刚开始run启动容器时,并不知道里面需要映射哪些端口,等容器已创建了,想映射端口. 当然可以通过先commit成镜像,然后再次run时指定端口,但会生成中间的镜像,对于有 ...

  7. firewalld添加/删除服务service,端口port

    启动CentOS/RHEL 7后,防火墙规则设置由firewalld服务进程默认管理. 一个叫做firewall-cmd的命令行客户端支持和这个守护进程通信以永久修改防火墙规则. # firewall ...

  8. vue-cli3x4x修改本地端口port

    一.推荐方法 "scripts": { "serve": "vue-cli-service serve --port 3000", &quo ...

  9. delphi 判断端口(Port)是否被占用(转载)

    function IsPortUsed(aPort: Integer): Boolean; var _vSock: TSocket; _vWSAData: TWSAData; _vAddrIn: TS ...

  10. 阿里云 全部端口port

随机推荐

  1. 047_SOQL 基本查询总结

    User currentUser = [SELECT Id, Profile.Name,UserRole.Name FROM User WHERE Id = :UserInfo.getUserId() ...

  2. Unity连接海康摄像头(shader转码)硬解码

    1.第一种方法 之前写过就不写了给个地址 2.第二种方法 用海康的SDK (shader转码) 先上效果 demo下载地址: 点击下载 开启多个摄像头的话 第二种比一种流畅

  3. 根据指定月份,打印该月份所属的季节。 3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12, 1, 2 冬季 if和switch各写一版

    1.public class Month{ public static void main(String args[]){ for (int i = 1;i <= 12 ;i++ ) { if ...

  4. 解决java.sql.SQLException: null, message from server: "Host 'XXX' is not allowed to connect异常

    Sqoop连接MySQL报异常.这个异常是数据库只允许localhost或127.0.0.1访问,不允许远程访问.我用的本机IP都不行. 解决办法:修改访问权限即可. 打开cmd,进入mysql fl ...

  5. ZSTUOJ刷题⑥:Problem 3535.--模拟简单计算器

    3535: 模拟简单计算器 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 4634  Solved: 1652 Description 程序模拟简单运算器 ...

  6. Linux 使用Squid部署代理缓存服务

    代理缓存服务 Squid是Linux系统中最为流行的一款高性能代理服务软件,通常用作Web网站的前置缓存服务,能够代替用户向网站服务器请求页面数据并进行缓存.简单来说,Squid服务程序会按照收到的用 ...

  7. not eligible for getting processed by all BeanPostProcessors

    描述 这个BUG大的起源是我上线以后,在后台看日志的时候发现一行奇怪的INFO日志: 2022-06-09 23:34:24 [restartedMain] [org.springframework. ...

  8. react实现转盘动画

    转盘动画方法如下: /** * 点击转动转盘 */ const turnCircle = () => { let runDeg = +(Math.random() * 360).toFixed( ...

  9. mysql 参数配置

    https://www.jb51.net/article/48082.htm https://www.cnblogs.com/angryprogrammer/p/6667741.html

  10. 20200925--矩阵乘法(奥赛一本通P94 多维数组)

    计算两个矩阵的乘法.n*m阶的矩阵A乘以m*k阶的矩阵B得到的矩阵C是n*k阶的,且C[i][j]=A[i][0]*B[0][j]+A[i][1]*B[1][j]+...+A[i][m-1]*B[m- ...