gojs 流程图框架-节点装饰器模板(二)

2023-11-13

上一章我们了解了如何使用 gojs 完成基本的节点和连接线的绘制, gojs 中还可以对节点或边进行自由拖动, 编辑等功能; 本章将基于上一章编写的流程图代码, 为这些节点设置装饰器模板

完成后的效果图:

建议下载源码, 对照本文进行学习, 源码地址: github.com/muzqi/sampl…

选择节点装饰器

在默认情况下, 我们用鼠标点击某个节点. 该节点会被一个蓝色的框所包裹,

这个框就是 选择节点装饰器, 如果我们想改变这个框的样式, 我们就需要为节点设置模板

编写模板

const nodeSelectionAdornmentTemplate =
  // [1]
  $(go.Adornment, go.Panel.Auto,
    // [2]
    $(go.Shape, {
      fill: null,
      stroke: 'yellow',
      strokeWidth: 1,
      strokeDashArray: [6, 6, 2, 2]
    }),
    
    // [3]
    // { width: 500, height: 200 }
    $(go.Placeholder))
复制代码

代码注释:

  1. 我们使用 go.Adornment 对象, 创建一个模板, 这实际上跟我们之前学到的节点模板思路是完全一样的, 定义它的布局方式, 再定义它的形状等属性
  2. 定义形状, 在这里我们定义了一个描边为黄色的虚线选择节点装饰器
  3. 设置 go.Placeholder 对象的目的是, 让装饰器自适应节点的大小; 反之, 我们可以自定义装饰器的大小

引入模板

我们来到定义节点模板的地方, 并为 go.Node 对象设置装饰器模板

diagram.nodeTemplateMap.add('node1',
    $(go.Node, go.Panel.Position,
        // ...
        {
            // [1]
            selectable: true,
            // [2]
            selectionAdornmentTemplate: nodeSelectionAdornmentTemplate
        }
        // ...
    )
)
复制代码

代码注释:

  1. selectable, 表示该节点是否能被选中, 默认为 true
  2. 我们将定义好的模板赋值给 selectionAdornmentTemplate 属性, 此时我们再回到页面, 点击节点


调整节点大小装饰器

用过 ps 的同学应该都知道, Ctrl + T 后, 能够唤出调整大小的操作装饰器, 我们现在需要给节点添加一个这样的装饰器, 让它支持 resize 操作

编写模板

const makeNodeResizeShapeOption = (cursor, alignment) => ({
    cursor,
    alignment,
    desiredSize: new go.Size(12, 12),
    fill: 'lightyellow',
    stroke: 'yellow'
})

const nodeResizeAdornmentTemplate =
    // [1]
    $(go.Adornment, go.Panel.Spot,
        $(go.Placeholder),
        
        // [2]
        $(go.Shape, makeNodeResizeShapeOption('nw-resize', go.Spot.TopLeft)),
        $(go.Shape, makeNodeResizeShapeOption('ne-resize', go.Spot.TopRight)),
        $(go.Shape, makeNodeResizeShapeOption('se-resize', go.Spot.BottomLeft)),
        $(go.Shape, makeNodeResizeShapeOption('sw-resize', go.Spot.BottomRight))
)
复制代码

代码注释:

  1. 同样, 我们还是使用 go.Adornment 来定义装饰器; 在这里, 我们布局方式使用了 go.Panel.Spot
  2. 我们定义了四个 go.Shape, 来表示装饰器的四个拖拽顶点

引入模板

diagram.nodeTemplateMap.add('node1',
    $(go.Node, go.Panel.Position,
        // ...
        {
            resizable: true,
            resizeAdornmentTemplate: nodeResizeAdornmentTemplate
        }
        // ...
    )
)
复制代码

resizeObjectName

调整大小装饰器引入时还可以传入一个 resizeObjectName 值, 表示指定需要应用装饰器的元素, 这个元素可以是 go.Shape go.Picture go.Text 任何 gojs 的元素

diagram.nodeTemplateMap.add('node1',
    $(go.Node, go.Panel.Position,
        // ...
        {
            resizable: true,
            // [1]
            resizeObjectName: 'TEXT'
            resizeAdornmentTemplate: nodeResizeAdornmentTemplate
        },
        // ...
        
        $(go.TextBlock, 
            // [2]
            { name: 'TEXT' }
            // ...
        )
    )
)
复制代码

代码注释:

  1. 设置 resizeObjectNameTEXT
  2. 将一个文字块元素的名字设置为 TEXT

如上图所示, 只有文字块被允许设置大小了


旋转节点装饰器

相比较前面的装饰器, 旋转节点装饰器存在一个巨坑;你会发现当你绘制出一个把手后, 你除了使用 Position 来绝对定位它, 否则你很难将其相对定位居中展示

官方的实例(其实他并没有被写到文档中去, 而是在demo文件中找到的方法)是将 go.RotatingTool 这个对象给改写掉了, 在初始化的时候, 就将把手的位置默认居中

请看以下代码:

编写模板

// [1]
const makeTopRotatingTool = () => (
    class TopRotatingTool extends go.RotatingTool {
        updateAdornments(part) {
            go.RotatingTool.prototype.updateAdornments.call(this, part)
            var adornment = part.findAdornment('Rotating')
            if (adornment !== null) {
                // [2]
                adornment.location = part.rotateObject.getDocumentPoint(new go.Spot(0.5, 0, 0, -30))  // above middle top
            }
        }

        rotate(newangle) {
            go.RotatingTool.prototype.rotate.call(this, newangle + 90)
        }
    }
)

// [3]
const nodeRotateAdornmentTemplate =
    $(go.Adornment,
        $(go.Shape, 'Circle',
            {
                cursor: 'pointer',
                desiredSize: new go.Size(7, 7),
                fill: 'lightyellow',
                stroke: 'yellow'
            }),

        $(go.Shape,
            {
                geometryString: 'M3.5 7 L3.5 30',
                isGeometryPositioned: true,
                stroke: 'yellow',
                strokeWidth: 1.5,
                strokeDashArray: [4, 2]
            })
    )
    
// [4]
const diagram = $(go.Diagram, 'diagram', {
    'initialContentAlignment': go.Spot.Center,
    'undoManager.isEnabled': true,
    'rotatingTool': $(makeTopRotatingTool())
})
复制代码

代码注释:

  1. 我们重新定义了一个类, 这个类继承了 go.RotatingTool
  2. 我们在这个类中, 定义了把手的位置默认是在顶部居中
  3. 使用 go.Adornment 对象制作模板, 这个模板只负责把手最终长成什么样子
  4. 在初始化 diagram 的时候, 引用自定义的工具类 makeTopRotatingTool

引用模板

diagram.nodeTemplateMap.add('node1',
    $(go.Node, go.Panel.Position,
        // ...
        {
            rotatable: true,
            // [1]
            // rotateObjectName: 'TEXT'
            rotateAdornmentTemplate: nodeRotateAdornmentTemplate,
            // [2]
            locationSpot: go.Spot.Center
        },
        // ...
    )
)
复制代码

代码注释:

  1. 同 @调整节点大小装饰器, 旋转节点装饰器也有 rotateObjectName 属性, 用法与效果和前者是一模一样的
  2. locationSpot 属性决定了节点旋转的锚点, 目前设置为以中心点旋转

拖拽创建连接线

前面我们已经将所有节点的装饰器添加完成, 最后, 我们需要实现从一个节点到另一个节点, 手动拖出连接线的功能;

为了更清晰的展示这个功能, 我们重新建立一块画布, 单独讲解;

实现原理

在 gojs 模板中, 任何一个元素, 都具备这三个属性:

  • portId 该 port 点的名称
  • fromLinkable 表示是否允许该节点接收拖过来的连接线
  • toLinkable 表示是否允许从该节点拖出连接线

只要具备这三个属性, 任何元素都能够拖出或者接收连接线, 并使两个节点产生连接关系

简单实现

<div id="port" style="width: 1000px; height: 500px"></div>
复制代码
const portDiagram = $(go.Diagram, 'port', {
  'initialContentAlignment': go.Spot.Center,
  'undoManager.isEnabled': true
})

// 指定被创建的连接线的模板
portDiagram.linkTemplate = $(go.Link,
  $(go.Shape, { stroke: 'black', strokeWidth: 3 })
)

// 指定被创建的节点的模板
portDiagram.nodeTemplate = $(go.Node,
  new go.Binding('position'),

  $(go.Shape,
    {
      fill: 'blue',
      fromLinkable: true,
      toLinkable: true
    },
    new go.Binding('portId', 'key')
  )
)

portDiagram.model = new go.GraphLinksModel(
  [
    {
      key: '1',
      position: new go.Point(500, 0)
    },
    {
      key: '2',
      position: new go.Point(0, 0)
    }
  ]
)
复制代码

细心的同学就会发现了, 现在我们的整个节点作为一个 port, 只要鼠标点击拖动节点, 就会拉出一条连接线, 但如果我们需要移动节点怎么办?

所以通常情况下, 我们会在节点中绘制几个点, 让这几个点具备拖拽接收连接线的功能;

改写上面的代码:

// ...

const makePort = (portId, spot) => (
  $(go.Shape, {
    cursor: 'pointer',
    fill: 'red',
    width: 10,
    height: 10,
    alignment: spot,
    portId,
    fromLinkable: true,
    toLinkable: true
  })
)

portDiagram.nodeTemplate =
  $(go.Node, go.Panel.Spot,
    new go.Binding('position'),

    $(go.Shape, { fill: 'blue' }),

    makePort('T', go.Spot.Top),
    makePort('B', go.Spot.Bottom),
    makePort('L', go.Spot.Left),
    makePort('R', go.Spot.Right),
  )
  
// ...
复制代码

实现效果如下:


下章继续讲解, 连接线的装饰器模板

(未完待续)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gojs 流程图框架-节点装饰器模板(二) 的相关文章

随机推荐

  • linux删除文件后硬盘空间不释放

    查看被删除了的所有文件 lsof n grep deleted 杀死这些文件的delete进程 释放空间 lsof n grep deleted awk print 2 xargs kill 9 接着再运行lsof n data grep
  • 模糊神经网络

    参考 https wenku baidu com view 94f77a7384868762cbaed58f html https wenku baidu com view 22590c72cc17552706220818 html 1 模
  • 新唐M0 内核 FLASH操作认识和总结

    本文不对代码做详细解析 先说结论 和常见问题 结论 结论1 FLASH在操作的时候 需要先 擦除 然后在 写入 结论2 擦除需要一整块擦除 不能只擦除某几个字节 结论3 写入是可以按照字节这样写入的 但是 结论1 的存在 导致写入也整片写入
  • linux syslog函数,Linux syslog相关函数详解

    介绍 syslog是Unix系统的日志系统 可以将日志记录在本地系统中 一个完整的syslong日志包含如下信息 程序模块 严重性 时间 主机名 进程名 进程ID 正文 syslong相关函数 1 openlog 函数 调用openlog
  • 迅为IMX6ULL-从C++到QT系统移植(QT视频他来了~)

    零基础的QT视频他来了 1 主打零基础入门 手把手教学 从C 到QT系统移植 带你打通QT的任督二脉 2 独创的框架学习法 先掌握整体的QT开发流程 然后在逐一击破 3 从Windows上位机开发 到Linux界面开发 再到手机APP开发
  • java中最小生成树的实现

    最小生成树的实现 import java util ArrayList import java util List public class ShortestTree int dataMap 1 1 10 1 30 100 1 1 5 1
  • java 数组追加数据

    想要追加数据 需要的流程是 数组 gt List gt 数组 案例 import java util ArrayList import java util Arrays import java util List public class
  • [MATLAB]Jacobi迭代

    MATLAB代码 关于使用雅可比迭代法求线性方程组的数值解 jacobi m 定义Jacobi迭代函数 function x n jacobi A b x0 eps 计算迭代矩阵 D diag diag A L tril A 1 U tri
  • Docker入门到实践 (六) docker网络模式详解以及容器间的网络通信

    文章目录 一 前言 二 docker网络模式介绍 1 默认网络 1 1 bridge网络模式 1 2 host网络模式 1 3 none网络模式 1 4 container网络模式 2 自定义网络 2 1 创建网络 2 2 连接网络 2 3
  • 微软收购暴雪的野心:与索尼争雄 重金布局元宇宙

    1月18日 微软发布声明称 将以全现金方式斥资687亿美元收购游戏巨头动视暴雪 这将成为微软有史以来规模最大的一笔收购 同时也将改写游戏行业的收购纪录 完成这笔收购之后 使命召唤 魔兽世界 糖果传奇 暗黑破坏神 守望先锋 等脍炙人口的作品将
  • element-ui el-cascader 级联选择器 联动默认值

    在使用 element ui 的 el cascader 组件根据后台返回的数据 需要展示一个默认值 官网给出的例子https element eleme cn 2 0 zh CN component cascader 借鉴了一下 话不多说
  • hexo博客搭建-背景知识(二)

    yum与rpm的区别 rpm适用于所有环境 而yum要搭建本地yum源才可以使用 yum是上层管理工具 自动解决依赖性 而rpm是底层管理工具 gcc cc c g 命令行详解 gcc包含的c c 编译器 gcc cc c g gcc和cc
  • JDK8 网络Net包研究(一)

    网络基础 1 国际标准化组织的OSI 开放式系统互联模型 七层模型 2 TCP IP协议 组 四层模型 3 TCP IP协议组 一组包括TCP协议和IP协议 UDP协议 ICMP协议和其他一些协议的协议组 网络层 IP协议 gt 网络互连协
  • sqlserver存储过程基本语法

    转载自 sqlserver存储过程的基本语法 1 定义变量 简单赋值 declare a int set a 5 print a 使用select语句赋值 declare user1 nvarchar 50 select user1 张三
  • ElasticSearch——全文检索

    ElasticSearch 全文检索 来源 尚硅谷 谷粒商城高级篇 一 简介 官网 https www elastic co cn what is elasticsearch 全文搜索属于最常见的需求 开源的 Elasticsearch 是
  • TypeScript学习(一):快速入门

    文章目录 一 TypeScript 简介 1 TypeScript 是什么 2 TypeScript 与 JavaScript 的区别 3 JavaScript 的缺点 4 为什么使用 TypeScript 二 TypeScript 开发环
  • 软件设计命名规范

    1 命名约定 Pascal和Camel命名约定 编程的命名方式主要有Pascal和Camel两种 Pascal 每个单词的首字母大写 例如ProductType Camel 首个单词的首字母小写 其余单词的首字母大写 例如productTy
  • IDA使用之旅(一)用IDA查看最简单的sys文件

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家拍砖 本系列内容是我根据 知其所以然论坛 博主录制的学习视频 做的笔记 使用的IDA软件版本 IDA pro 5 5 参考下载地址 http w
  • 使用Maven插件整合protocol buffer

    本来自己在网上找如何使protocol buffer在IDE 我用的是IDEA 上使用的 结果搜索出来的都不尽人意 因为都太粗略了 没有重点的去阐述 所以最后还是决定自己搜索相关的Maven插件 再慢慢地摸索 费了我好多的时间啊 本人小白
  • gojs 流程图框架-节点装饰器模板(二)

    上一章我们了解了如何使用 gojs 完成基本的节点和连接线的绘制 gojs 中还可以对节点或边进行自由拖动 编辑等功能 本章将基于上一章编写的流程图代码 为这些节点设置装饰器模板 完成后的效果图 建议下载源码 对照本文进行学习 源码地址 g