流程节点图形变化

news2024/11/27 12:55:37

一、背景

(1)流程节点为矩形,只有上下左右四个连接点。

(2)支持移动,放大缩小,连接线。

二、需求

(1)流程节点支持图形变化。

(2)支持节点边框样式设置。

(3)图形支持圆角

三、方案设计

(1) 根据开发成本考虑,仅支持仍为四个操作点(上、下、左、右)的图形。这样线的连接,绘制操作等原本根据矩形编写的逻辑都不用重新编写,能够保持一致的逻辑。支持:矩形、圆角矩形、菱形、正六边形、平行四边形,圆形,梯形,以及两个不规则图形。

(2)保持原本矩形四个连接点、以及其他逻辑

1、矩形作为固定层,无边框任然存在,将svg图形和矩形同时绘制并重叠,svg图形展示边框,但是操作点的位置,操作栏任然根据矩形去计算。(实际还是矩形,只是图形展示使用svg去绘制展示了)以圆形为例:

矩形:

 

svg 

 

2、svg图形上的点是要根据矩形去计算的。计算出来的svg可能是会超出矩形。例如:梯形和平行四边形。并且需要支持放大缩小,因此svg图形上的点需要根据矩形的宽高去计算。

矩形:

 

svg 

 

大致:

 3、因为svg超出范围无法显示,故需要对svg背景做一个根据矩形的一个偏移,将矩形的四个点放置在svg图形上。

(3)缩放图标的位置根据图形要做调整,例如梯形,因为原本缩放图标的位置是根据矩形计算的,一些超出矩形的图形还是根据矩形计算的话会出现距离上的问题,因此需要根据具体svg图形做一个调整。 

未调整:

调整:

 (4)图形弧形弧度角度要考虑

因为不同的角度计算出来的图形形状是不一样的,因为需要跟UI沟通好

(5)keepLength根据图形去调整

此属性是绘制线,线开段的那段长度,不同图形可能会出现挡住,过短的情况,因此要根据具体图形做调整。

(6)svg阴影

1、有=》无阴影区别

2、方案

a、可以采用svg方案,filter

 <defs>
      <filter
        id="outset-shadow"
        height="150%"
        width="150%"
        x="-25%"
        y="-25%"
        filterUnits="userSpaceOnUse"
      >
        <feMorphology
          operator="dilate"
          radius="3"
          in="SourceAlpha"
          result="thicken"
        />
        <feGaussianBlur in="thicken" stdDeviation="3" result="blurred" />
        <feFlood flood-color="grey" result="glowColor" />
        <feComposite
          in="glowColor"
          in2="blurred"
          operator="in"
          result="softGlowColored"
        />
        <feComposite
          in="softGlowColored"
          in2="SourceGraphic"
          operator="out"
          result="outSoftGlowColored"
        />
        <feMerge>
          <feMergeNode in="outSoftGlowColored" />
          <feMergeNode in="SourceGraphic" />
        </feMerge>
      </filter>
    </defs>
    <defs>
      <filter id="blur" filterUnits="userSpaceOnUse">
        <feOffset result="offset" in="SourceAlpha" />
        <feGaussianBlur result="blur" stdDeviation="5" />
        <feFlood result="flood" flood-color="#eee" />
        <feComposite result="composite" operator="in" in2="blur" />
        <feBlend result="blend" in="SourceGraphic" />
      </filter>
    </defs>

b、 也可以采用css属性

filter: drop-shadow(0px 0px 4px var(--shadow))

 本方案采用的是简洁的css属性。

(7)样式根据svg提供的去实现即可。其中宽度,需要对图形的坐标做一个偏移(边框宽度的偏移)才能完整展示出来宽度。

 三、svg图形绘制

 (1)分析图形,发现图形很多都是对称图形,因此可以进行对称点计算

1、获取点关于直线的对称点坐标

/** @name 获取点关于直线的对称点坐标 */
export function getPointSymmetryPoint(
  point,
  symmetryLine = {
    A: 0,
    B: 0,
    C: 0,
  },
) {
  const { A, B, C } = symmetryLine;
  const x =
    point.x -
    (2 * A * (A * point.x + B * point.y + C)) /
      (Math.pow(A, 2) + Math.pow(B, 2));
  const y =
    point.y -
    (2 * B * (A * point.x + B * point.y + C)) /
      (Math.pow(A, 2) + Math.pow(B, 2));
  return {
    x,
    y,
  };
}

2、获取两点的中点坐标 

/** @name 获取两点的中点坐标 */
export function getMiddlePoint(point1, point2) {
  return {
    x: (point1.x + point2.x) / 2,
    y: (point1.y + point2.y) / 2,
  };
}

 3、弧形,绘制二次贝尔曲线,需要求控制点(已知起点、终点、中点求控制点),此公式在绘制正六边形、弧形矩形、菱形、右直角矩形中会用到,因为需要弧形的顶点落在矩形上

/** @name 已知起点、终点、中点求控制点 */
export function threePointGetControlPoint(sPoint, ePoint, mPoint) {
  const x = 2 * mPoint.x - (sPoint.x + ePoint.x) / 2;
  const y = 2 * mPoint.y - (sPoint.y + ePoint.y) / 2;
  return { x, y };
}

 (2)节点宽高是已知,根据节点宽高,图形的角度,根据三角函数去画图计算点坐标,对称点根据函数去计算,弧形使用二次贝尔曲线绘制(弧形的起点、终点,终点需要进行假设)。以梯形为例: 

<template>
  <g>
    <path
      :style="computedStyle"
      :class="computedClass"
      class="node-figure"
      :d="path"
    />
    <foreignObject
      :x="levelPoint.x"
      :y="levelPoint.y"
      class="flower-node-level"
      v-if="level && showLevel"
    >
      <span>{{ level }}</span></foreignObject
    >
  </g>
</template>
<script>
import { Vue, Component, Prop } from 'vue-property-decorator';
import { getPointSymmetryPoint } from './utils.js';
@Component()
//梯形
export default class LadderShaped extends Vue {
  /** @type { number } */
  @Prop() width;
  /** @type { number } */
  @Prop() height;
  /** @type { boolean } */
  @Prop({ type: Object, default: () => ({}) }) borderStyle;
  @Prop({ type: Number, default: 1 }) level;
  @Prop({ type: Object, default: () => ({}) }) computedStyle;
  @Prop({ type: Array, default: () => [] }) computedClass;
  @Prop({ type: Boolean, default: false }) showLevel;
  get offset() {
    if (!this.borderStyle) return 1;
    return Number(this.borderStyle.width);
  }
  get levelPoint() {
    const tan = Math.tan((this.angle * Math.PI) / 180);
    const control12 = this.handleOffsetPoint({
      x: this.height / tan - 5,
      y: -1,
    });
    return control12;
  }
  angle = 60;

  get topXOffset() {
    return 6;
  }
  get bottomXOffset() {
    return 10;
  }
  get path() {
    //tan60值
    const tan = Math.tan((this.angle * Math.PI) / 180);
    const sin = Math.sin((this.angle * Math.PI) / 180);
    const cos = Math.cos((this.angle * Math.PI) / 180);
    const point1 = this.handleOffsetPoint({
      x: this.height / tan - cos * this.topXOffset,
      y: sin * this.topXOffset,
    });
    const point2 = this.handleOffsetPoint({
      x: this.height / tan + this.topXOffset,
      y: 0,
    });
    const point7 = this.handleOffsetPoint({
      x: this.bottomXOffset,
      y: this.height,
    });
    const point8 = this.handleOffsetPoint({
      x: this.bottomXOffset * cos,
      y: this.height - this.bottomXOffset * sin,
    });
    //计算对称点
    const line = {
      A: 1,
      B: 0,
      C: -((this.width + this.height / tan) / 2) - this.offset,
    };
    const point3 = getPointSymmetryPoint(point2, line);
    const point4 = getPointSymmetryPoint(point1, line);
    const point5 = getPointSymmetryPoint(point8, line);
    const point6 = getPointSymmetryPoint(point7, line);
    const control12 = this.handleOffsetPoint({
      x: this.height / tan,
      y: 0,
    });
    const control34 = this.handleOffsetPoint({
      x: this.width,
      y: 0,
    });
    const control56 = this.handleOffsetPoint({
      x: this.width + this.height / tan,
      y: this.height,
    });
    const control78 = this.handleOffsetPoint({
      x: 0,
      y: this.height,
    });
    return `M ${point1.x} ${point1.y}
            Q ${control12.x} ${control12.y} ${point2.x} ${point2.y}
            L ${point3.x} ${point3.y}
            Q ${control34.x} ${control34.y} ${point4.x} ${point4.y}
            L ${point5.x} ${point5.y}
            Q ${control56.x} ${control56.y} ${point6.x} ${point6.y}
            L ${point7.x} ${point7.y}
            Q ${control78.x} ${control78.y} ${point8.x} ${point8.y}
            Z`;
  }
  handleOffsetPoint({ x, y }) {
    return { x: x + this.offset, y: y + this.offset };
  }
}
</script>

<style lang="less"></style>

 四、svg相关知识

 (1)基础图形

1、矩形(圆角)

 <rect
    width="200"
    height="100"
    rx="20"
    ry="40"
  >

2、 圆形

<circle
    cx="60"
    cy="80"
    r="50"
  >

3、 path

 <path
    d="M 10 10 L 50 40 L 100 10"
    stroke="blue"
    fill="none"
  >

  • M: 起始点坐标,moveto 的意思。每个路径都必须以 M 开始。M 传入 xy 坐标,用逗号或者空格隔开。
  • L: 轮廓坐标,lineto 的意思。L 是跟在 M 后面的。它也是可以传入一个或多个坐标。大写的 L 是一个绝对位置。
  • l: 这是小写 L,和 L 的作用差不多,但 l 是一个相对位置。
  • H: 和上一个点的Y坐标相等,是 horizontal lineto 的意思。它是一个绝对位置。
  • h: 和 H 差不多,但 h 使用的是相对定位。
  • V: 和上一个点的X坐标相等,是vertical lineto 的意思。它是一个绝对位置。
  • v: 这是一个小写的 v ,和大写 V 的差不多,但小写 v 是一个相对定位。
  • Z: 关闭当前路径,closepath 的意思。它会绘制一条直线回到当前子路径的起点

 (2)图形样式设置方法

1、属性样式:直接在元素属性上设置样式

<rect
    x="100"
    y="100"
    width="200"
    height="100"
    fill="pink"
  />

2、 内联样式:把所有样式写在 style 属性里

<rect
    x="100"
    y="100"
    width="200"
    height="100"
    style="fill: pink;"
  />

3、 内部样式:将样式写在 <style> 标签里

<style>
  .rect {
    fill: pink;
  }
</style>

<svg width="400" height="400" style="border: 1px solid red;">
  <rect
    x="100"
    y="100"
    width="200"
    height="100"
    class="rect"
  />
</svg>

 4、外部样式:将样式写在 .css 文件里,然后在页面中引入该 CSS 文件

(3)使用的样式设置

1、填充 fill:要填充图案颜色,可以设置 fill 属性

 <rect
    x="100"
    y="100"
    width="200"
    height="100"
    fill="greenyellow"
  />

2、 描边颜色 stroke

<rect
    x="100"
    y="100"
    width="200"
    height="100"
    fill="none"
    stroke="blue"
  />

3、 描边宽度 stroke-width

<rect
    x="100"
    y="100"
    width="200"
    height="100"
    fill="none"
    stroke="blue"
    stroke-width="10"
  />

4、 虚线描边 stroke-dasharray

边框的点线或者虚线样式,stroke-dasharray 接收一串数字,这串数字可以用来代表线的长度和空隙的长度,数字之间用逗号或者空格分隔。建议传入偶数个数字。但如果你传入了奇数个数字,SVG 会将这串数字重复一遍,使它的数量变成偶数个

 <line
    x1="30"
    y1="70"
    x2="300"
    y2="70"
    stroke="blue"
    stroke-dasharray="20 10"
  />

5、线帽 stroke-linecap:线帽就是线的起始点和结束点的位置

  • butt: 平头(默认值)
  • round: 圆头
  • square: 方头

6、拐角 stroke-linejoin:拐角就是折线的交接点

  • miter: 尖角(默认)
  • round: 圆角
  • bevel: 平角

(4)曲线

 Q命令可以用来绘制一条二次贝塞尔曲线,二次贝塞尔曲线需要一个控制点,用来确定起点和终点的曲线斜率。

Q x1 y1, x y 或者 q x1 y1, x y

 1、参数:x、y为终点位置,x1、y1为控制点

2、起点就是M命令

<path d="M50 100 Q 175 200 300 100" fill="none" style="stroke: #ff0000;"/>

五、效果图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/818849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第7期ThreadX视频教程:如何实现RTOS高效的任务管理,抢占式调度,时间片调度和零中断延迟(2023-07-31)

视频教程汇总帖&#xff1a;https://www.armbbs.cn/forum.php?modviewthread&tid110519 本期视频为大家分享高效的RTOS任务管理设计&#xff0c;通过这个点来引出抢占式调度&#xff0c;时间片调度&#xff0c;任务优先级设置和零中断延迟。 RTOS任务高效管理是我们使用R…

吃透《西瓜书》第四章 决策树定义与构造、ID3决策树、C4.5决策树、CART决策树

目录 一、基本概念 1.1 什么是信息熵&#xff1f; 1.2 决策树的定义与构造 二、决策树算法 2.1 ID3 决策树 2.2 C4.5 决策树 2.3 CART 决策树 一、基本概念 1.1 什么是信息熵&#xff1f; 信息熵: 熵是度量样本集合纯度最常用的一种指标&#xff0c;代表一个系统中蕴…

Python小红书旋转验证码识别

本周免费接了一个用户的需求&#xff0c;研究了一下小红书旋转验证码。刚开始小瞧了它&#xff0c;觉得它应该没有百度旋转验证码那么难&#xff0c;毕竟图像没有干扰&#xff0c;需要的训练样本就可以很少。然而事情并没有这么简单&#xff0c;所以记录一下。 首先看一下最终…

代码随想录算法训练营第二天| 977. 有序数组的平方y,螺旋矩阵 II,209. 长度最小的子数组

977. 有序数组的平方y 思路&#xff0c;原数组是有序的&#xff0c;但是因为负数平方后可能变无序了&#xff0c;因此利用双指针遍历原数组&#xff0c;比较 nums[left]*nums[left]和nums[right]*nums[right]谁更大&#xff0c;然后对新数组赋值 class Solution {public int…

uni-table行点击事件

uni-app 如何点击表格的行跳转到对应的页面 找到uni-ui的源码&#xff0c;在源码里面绑定事件 用$emit发射事件 在你使用了uni-table的组件中接受改事件 然后绑定调用成功

在中国区部署日志通2.0

前提条件 一个域名&#xff1a;使用此域名来访问日志通控制台提供aws iam 的ssl证书 &#xff0c;而且必须跟域名相关联具有四个子网&#xff08;两个公有子网和两个私有子网&#xff09;和NAT网关的VPC 步骤 1.创建ACM证书 1.1 请求公有证书 1.2 配置域名 1.3 新申请的证书记…

Python自动化测试之函数知识讲解

一、前言 本文章主要讲解Python中的函数定义调用、参数分类、函数使用、作用域以及内置函数等内容&#xff0c;是函数专题讲解&#xff0c;这些都是python自动化学习中的基础哟&#xff0c;只有学好了基础才会有利于后面自动化的学习哟。这里我不多说废话了&#xff0c;直接进入…

GO学习之 多线程(goroutine)

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 文章目录 GO系列前言一、并发介绍1.1 进程和线程和协程1.2 并发和并行 二、goroutine介绍三…

Android 中 app freezer 原理详解(二):S 版本

基于版本&#xff1a;Android S 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理&#xff0c;为什么叫这个名字&#xff0c;而不…

HTTP协议 和 HTTPS协议的区别(4点) HTTPS如何使用SSL/TLS协议加密过程 CA证书干啥的

&#xff08;一&#xff09;HTTP协议 和 HTTPS协议的区别&#xff08;4点&#xff09;&#xff1a; 1. HTTP协议的端口号是80&#xff0c; HTTPS协议的端口号是443 2. HTTP协议使用的URL是以 http:// 开头&#xff0c;HTTPS协议使用的URL是以https://开头 3. HTTP协议和HTTP…

Qt 中操作xml文件和JSON字符串

文章目录 1、概述1.1、xml介绍1.2、json介绍 2、xml文件增删改查2.1、写xml文件内容2.2、读xml文件内容2.3、删除xml文件内容2.4、修改xml文件内容 3、构建JSON字符串3.1、JSON字符串排版4、剪切板操作 1、概述 1.1、xml介绍 XML 指可扩展标记语言&#xff08;EXtensible Mark…

“效能指标”,该由谁来定义?| 谈效风生

第5期&#xff1a;效能指标&#xff0c;该由谁来定义&#xff1f; 回顾上期《「自动化」聊起来简单&#xff0c;做起来难》我们聊了聊如何打造「自动化」的事&#xff0c;这也是真正实现研发效能提升的必要条件。从单点自动化提升效率&#xff0c;到全工具链自动化&#xff0c;…

常微分方程建模R包ecode(二)——绘制相速矢量场

本节中我们考虑一个更为复杂的常微分方程模型&#xff0c; d X C d t ν ( X A Y A ) − β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ) ⋅ X C , ( 1 ) d Y C d t β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ρ ) ⋅ Y C , ( 2 ) d X A d t g ⋅ X C − β ⋅ X A ⋅ ( Y C Y A …

2023最新版本~十分钟零基础搭建EMQX服务器

购买服务器 已知服务器大厂商 1 阿里云 点击直接访问 2 华为云点击直接访问 3 腾讯云 点击直接访问 还是比较推荐大公司 不会跑路 这里我购买的是一年的华为云服务器(新用户 64一年) 镜像推荐乌班图18 登陆服务器&#xff08;需要重置密码&#xff01;&#xff01;&…

Ansible自动化运维工具 —— Playbook 剧本

playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 &#xff08;2&#xff09;Variables&#xff1a;变量 &#xff08;3&#xff09;Templates&#xff1a;模…

ubuntu16.04忘记密码了怎么办,亲测有效

由于装载Ubuntu系统的电脑&#xff08;非虚拟机&#xff09;好久没有用&#xff0c;忘记了密码&#xff0c;只能进行密码重置&#xff0c;亲测有效&#xff1a; 1.首先ubuntu系统开机&#xff0c;期间按着shift键不放&#xff0c;选择高级选项。 2.enter键进入如下界面&#x…

python-occ入门指北

0、系统环境: Win10 在Windows环境中玩PythonOCC比较简单的方式是使用anaconda的cmd prompt或者powershell的prompt&#xff0c;这里我用的是cmd。PowerShell也有很多粉丝&#xff0c;但是个人真的觉得这个东西挺鸡肋的。 另外在Win10或Win11上另一个玩法是使用WSL2&#xff…

【漏洞挖掘】Xray+rad自动化批量漏洞挖掘

文章目录 前言一、挖掘方法二、使用步骤工具安装使用方法开始挖掘 总结 前言 自动化漏洞挖掘是指利用计算机程序和工具来扫描、分析和检测应用程序、网络和系统中的安全漏洞的过程。这种方法可以帮助安全专家和研究人员更高效地发现和修复潜在的安全威胁&#xff0c;从而提高整…

企业级开发中协同开发与持续集成持续部署

文章目录 1 创建代码仓库2 使用git协同开发2.1 独立团队开发2.2 多团队开发git工作流 2 持续集成和持续部署2.1 创建docker镜像2.2 使用coding构建 1 创建代码仓库 每个项目有唯一的代码仓库&#xff0c;所以不是每个开发者都需要创建一个代码仓库&#xff0c;一般都是项目负责…

Node版本自由切换之nvm安装教程

1.nvm安装包下载&#xff0c;这里推荐1.1.7版本 https://github.com/coreybutler/nvm-windows/releases/download/1.1.7/nvm-setup.zip 2.解压后运行exe文件&#xff0c;一路默认就可以了&#xff0c;自定义的话&#xff0c;文件路径不要有中文&#xff1b; 3.安装之后使用命…