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 流程图框架-节点装饰器模板(二) 的相关文章

  • 普通 JSON 到 GraphSON 格式

    我有两个问题 我在哪里可以真正找到 GraphSON 文件的基本格式 保证 gremlin 控制台成功加载 我正在尝试将 JSON 大约有 10 20 个字段 转换为另一个可以由 gremlin 查询的文件 但我实际上找不到有关 graph
  • ~~(“双波浪线”)在 Javascript 中做什么?

    我今天在查看一个在线游戏物理库时遇到了 运算符 我知道单个 是按位 NOT 这会使 成为 NOT 的 NOT 这会返回相同的值 不是吗 它会删除小数点后的所有内容 因为按位运算符会隐式地将其操作数转换为带符号的 32 位整数 无论操作数是
  • React:如何从 Material-UI TextField 组件获取值

    我有一个小型应用程序 其中包含 Form 组件 SubmitButton 组件和我的父组件 App js 当用户单击提交按钮时 我想获取表单组件上 3 个字段的值并将它们传递给我的 App js 组件 我不确定如何使用触发事件onClick
  • 当来源为 http 且目标 url 为 https 时,如何在本地网络中发出 POST 请求?

    我需要从 POS 销售点 http 向支付终端 https 发出 POST 请求 它们连接在我的本地网络中 当我向邮递员发出请求时 一切正常 但每当我从 POS 发出请求时 我都会收到错误 POSThttps 我的IP地址 8443 nex
  • 为什么这个对象的“forEach 不是函数”?

    这可能真的很愚蠢 但我不明白为什么这不起作用 var a cat large a forEach function value key map console log value 未捕获的类型错误 a forEach 不是函数 http j
  • Web SQL 将数据插入多行

    我尝试在 Web SQL 数据库中一次将变量插入多行 但使用我所知的所有方法时 我收到错误 INSERT INTO tab a b VALUES v1 v2 v3 v4 gt gt could not prepare statement 1
  • 如何将文本插入摩纳哥编辑器?

    我的应用程序中嵌入了摩纳哥代码编辑器 如何以编程方式在特定行上插入文本 var editor monaco editor create document getElementById container value First line n
  • Node.js 应用程序中的系统托盘图标

    这是我的 node js 应用程序中需要的 系统托盘图标 在应用程序工作期间更改此图标 单击图标后的菜单 创建带有登录 密码字段和确认按钮的窗口 这是我发现的 https github com appjs appjs https githu
  • .map() Javascript ES6 地图?

    你会怎么做 本能地 我想做 var myMap new Map thing1 1 thing2 2 thing3 3 wishful ignorant thinking var newMap myMap map key value gt v
  • 我可以在 Express POST 请求中进行 DOM 操作吗?

    我正在使用基本的 HTML CSS 前端 目前有一个登陆页面 上面有一个表单 可将 一些数据发送到数据库 当请求完成后 它期待某种响应 在这种情况下 我正在重新渲染页面 但是 我想用某种感谢消息替换表单 以便用户知道它已正确发送 我尝试过简
  • Puppeteer 错误 错误:等待选择器超时

    目前我有一个网站 其 HTML 中有此内容 我通过检查chrome开发者工具中的元素确认了这一点 div class hdp photo carousel div class photo tile photo tile large 我直观地
  • Ionic 和 Angular 2 - 拒绝应用“http://localhost:8100/build/main.css”中的样式,因为其 MIME 类型(“text/html”)不受支持

    My Ionic https en wikipedia org wiki Ionic mobile app framework 构建一直运行得很好 直到我想在我的 iPhone 上进行测试 所以我停止了我的服务器 做了一个ionic ser
  • 大型 AngularJS 应用程序设计

    我需要关于设计具有多个复杂模块的 AngularJS 应用程序的建议 并根据用户角色在身份验证和授权后加载模块 有些用户可以访问一个简单的模块 有些用户可以访问仪表板 有些用户可以访问 2 个以上的模块 我们已经确定了许多可以在不同模块中重
  • 最有用的 jQuery 原生 API 函数

    前 5 10 个最常用的 jQuery 本机 API 函数是什么 请不要建议 jQuery 函数本身 因为毫无疑问这是最常用的函数 如果可能的话 还请提供它们所涵盖的场景 提出这个问题的原因是我尝试创建一个类似 jQuery 的 API充足
  • Rails:包括外部 JavaScript

    我想使用 JavaScript 库 例如 jQuery 插件 我是否使用 Rails 资产管道 或者我应该将其包含在 javascript include tag 中 我有哪些选择以及推荐的做法是什么 您会仅在几个页面上还是在整个应用程序中
  • [对象窗口]是什么?

    谷歌翻译有一些书签可以让您一键翻译 例如 javascript var t window getSelection window getSelection document getSelection document getSelectio
  • 如何使用startsWith过滤并获取每个对象键的值?

    我试图通过获取每个键来过滤对象checkpoint并输出其值 目前 我只能输出键而不是值 下面 我有一个简单的对象 我正在使用过滤器和startsWith 我怎样才能得到这些值呢 var data practicals 0 checkpoi
  • 有没有办法防止 neDB 集合数组中的条目重复?

    var addNewUser function id chatId db update id id push users chatId function err numAffected code after the record is up
  • 我可以使用 javascript 生成 JSON 文件吗?

    我想在域 example1 com 上创建一个页面 并获取 解析另一个域 example2 com json json 上的 JSON 文件 可以使用 javascript 生成 json 文件 在 example2 com 上 吗 我认为
  • 与桌面浏览器相比,移动浏览器有多强大? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • 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