Svg Flow Editor 原生svg流程图编辑器(四)

news2024/9/23 15:22:25

系列文章

Svg Flow Editor 原生svg流程图编辑器(一)

Svg Flow Editor 原生svg流程图编辑器(二)

Svg Flow Editor 原生svg流程图编辑器(三)

Svg Flow Editor 原生svg流程图编辑器(四)

实现Echart统计图

        统计图的底层我们采用apache echarts 实现【Apache ECharts】,先封装GEchart,GEchart是我们的外层框架,支持形变、旋转,与Rect等svg元件类似的结构,但是内部是div实现:

        同时,样式设置上与svg还是有些不同,需要单独处理下。


  /**
   * 需要向外暴露 setOption 方法,供数据变化后重新渲染
   * @param option
   */
  private setOption() {
    if (!this.option) throw new Error(messageInfo.optionError);
    this.myChart.setOption(this.option);
    return this;
  }

  /**
   * 向外提供 update 方法,供用户在 option 变化后更新页面内容
   *  因 option 是引用地址,因此 可以不需要传递参数,从而实现数据更新
   * @returns
   */
  public update() {
    return this.setOption();
  }

        事件上,则是核心的 setOption 与 update 两个方法,update则是向外提供给用户更新时使用。同时,缩放会导致父节点尺寸变化,因此还需要监听尺寸变化实现动态Echart重绘,使用第三方库实现此功能【也可以使用 const ob = new ResizeObserver() 这个原生API实现哈,看自己的需求】:

import elementResizeDetectorMaker from "element-resize-detector";
var erd = elementResizeDetectorMaker();

// 监听元素尺寸变化,重新渲染echart 使得宽高自适应
erd.listenTo(this.div, () => this.myChart.resize());

        还需要封装一层插件,因为GEchart是核心类,不能直接提供给用户,也不便于结构管理:

// echarts 插件 多一层的原因是构建新的实例
export class SEchart {
  private draw: Draw;

  constructor(draw: Draw) {
    this.draw = draw;
  }

  /**
   * 初始化 Echart
   * @param option
   * @returns
   */
  public init(option: object) {
    return new GEchart(this.draw, option);
  }
}
// 关键!需要注册插件,提供 echart 绘制能力  
const echart = editor.plugin("echart");

// 初始化echart
const line = echart?.init(option); 

// 模拟数据更新
setTimeout(() => {
    data[0] = "123123mode";
    line?.update();
}, 1000);

        注意哈,class GEchart extends GraphCommon, GEchart 类继承了 Common 类,拥有元件的所有属性方法,包括 setWidth position 等;而 SEchart 则是隔离用户触碰核心类,同时也给用户注册多实例提供可能,也是对插件化提供支持。

        Echart 的事件处理,则是基于EventBus 实现实例的 emit on 操作:

this.event = new EventBus();

// Echarts click
this.myChart.on("click", (params: object) =>
    this.event.emit("click", params)
);

// Echarts 鼠标移出
this.myChart.on("mouseout", (params: object) =>
    this.event.emit("mouseout", params)
);

// Echarts 鼠标移入
this.myChart.on("mouseover", (params: object) =>
    this.event.emit("mouseover", params)
);

const echart = editor.plugin('echart')

const bar=echart.init(barOption)

bar.event.on('click',p=>{
    // p 是回调的参数
})

直角折线

        以下实现思路参考logicFlow直角折线思想,连接锚点mousedown事件中,我们要记录当前锚点的位置信息:

    // 1. 获取当前元素的宽高位置信息
    const width = graph.getWidth();
    const height = graph.getHeight();
    const x = graph.getX();
    const y = graph.getY();

    // 2. 需要计算起点位置 -- 锚点位置受padding影响
    const typeMap: { [key: string]: { sx: number; sy: number } } = {
      "0": { sx: x, sy: y + height / 2 },
      "1": { sx: x + width / 2, sy: y },
      "2": { sx: x + width, sy: y + height / 2 },
      "3": { sx: x + width / 2, sy: y + height },
    };
    const sx = typeMap[type].sx + 10;
    const sy = typeMap[type].sy + 10;

        使用editorBox来接收mousemove事件以实现流程的拖动,但是在拖动过程中,可能会经过多个元素,导致 offset 值得变化,需要做优化:

/**
 * 需要做位置矫正
 *  class='sf-editor-box-graphs' 正确
 *  class='sf-editor-box-graphs-main' 异常 偏移量加 offsetLeft offsetTop 值
 *  class='' 异常
*/

 

         绘制结束后,需要根据logicFlow的思想,构建出下面这个图形:

        实现的思路是根据线的起点、终点关联的元件计算得出:

实现关键代码:

 console.log("### 绘制最终折线,根据框的宽高位置信息获取基础数据");
    const eid = this.line.getAttribute("eid") as string;
    const sid = this.line.getAttribute("sid") as string;
    if (!eid) return this.lineBox.remove();

    // 不然处理折线的寻径算法
    this.line.setAttribute("stroke-dasharray", "");

    // 1. 获取 sid eid 构建 graph
    const Sgraph = new Graph(this.draw, sid);
    const Egraph = new Graph(this.draw, eid);

    // 2. 获取start的宽高 位置信息
    const sx = Sgraph.getX();
    const sy = Sgraph.getY();

    // 3. 获取 end 的宽高 位置信息
    const ex = Egraph.getX();
    const ey = Egraph.getY();

    // 4. 需要知道哪个元件在最后 也就是 graph x 最大
    const maxGrapg = sx > ex ? Sgraph : Egraph;

    // 5. 构建 OFFSET 的矩形 --- 受padding的影响
    const lx = Math.min(sx, ex) - OFFSET + 10;
    const ly = Math.min(sy, ey) - OFFSET + 10;
    this.setX(lx);
    this.setY(ly);
    this.lineBox.style.backgroundColor = "rgba(0,0,0,0.1)";
    // 6. 取消直线
    this.line.setAttribute("points", "");

    // 7. 设置宽高
    const mw = maxGrapg.getWidth();
    const mh = maxGrapg.getHeight();
    const mx = maxGrapg.getX();
    const my = maxGrapg.getY();

    // 自此,整个线的宽高= lx -> mx + mw + OFFSET
    const lw = mx - lx + mw + OFFSET + 10;
    const lh = my - ly + mh + OFFSET + 10;
    this.setWidth(lw);
    this.setHeight(lh);

         根据矩形框,找出所有可能路径关键点:

    // 做点的纠正-因为 计算得到的是 基于背景的 而线的绘制基于新的 div 坐标,需要做处理 【并且受 padding 的影响】
    const getX = (x: number) => x - lx + 10;
    const getY = (y: number) => y - ly + 10;

    // 起点
    const startType = typeMap[st]({ x: sx, y: sy, w: sw, h: sh });
    const startPoint = { x: getX(startType.x), y: getY(startType.y) };
    points.push(startPoint);

    // 终点
    const endType = typeMap[et]({ x: ex, y: ey, w: ew, h: eh });
    const endPoint = { x: getX(endType.x), y: getY(endType.y) };
    points.push(endPoint);

    // 如果存在间隙,则取偏移量的点,如果不存在间隙,则不取偏移量的点
    const intervalX = maxGrapg.getX() - minGraph.getX() + minGraph.getWidth();
    const intervalY = maxGrapg.getY() - minGraph.getY() + minGraph.getHeight();
    if (intervalX > OFFSET && intervalY > OFFSET) {
      // 取偏移量
      const startOffsetPoint = { x: getX(startType.ox), y: getY(startType.oy) };
      points.push(startOffsetPoint);

      const endOffsetPoint = { x: getX(endType.ox), y: getY(endType.oy) };
      points.push(endOffsetPoint);
    }

         轴线与边界的交点:

        A* 算法实现:


    const list: expandP[] = JSON.parse(JSON.stringify(points)); // 深拷贝简单实现
    var optimal: expandP[] = []; // 记录最优解

    // 计算当前点的最短路径
    const computedDistance = (p: expandP) => {
      console.group("开始 A* 算法");
      console.log("当前点", p);
      // 获取list中 x、y 相同的点,并计算最短路径
      const ps = list.filter((i: expandP) => i.x === p.x || i.y === p.y);
      // 循环当前可达的点,并计算距离
      ps.forEach((i: expandP) => {
        i.d = Infinity; // 默认无穷大
        // 并且计算传入的点与当前点的向量是否穿过矩形
        // 计算曼哈顿距离
        i.d = Math.abs(i.x - endPoint.x) + Math.abs(i.y - endPoint.y);
      });
      ps.sort((a, b) => (a.d as number) - (b.d as number));
      console.log("当前可达的点", ps);
      this.drawPoint(ps[0], "green");
      console.groupEnd();
      return ps[0];
    };

    /**
     * 【开始寻径算法】
     *  1. 找到当前点的 x y 相同的点作为可达点,
     *  2. 并且规定,当前点的可达距离为1,取到终点的曼哈顿距离
     *  3. 还需要判断当前两点的向量是否穿过矩形
     */
    const search = () => {
      const point = optimal.length ? optimal[optimal.length - 1] : startPoint; // 当前的最优解
      if (point.x === endPoint.x && point.y === endPoint.y)
        return console.log("A* 算法结束,最优路径为 => ", optimal);
      const optimalPoint = computedDistance(point);
      optimal.push(optimalPoint);
    };

        这样会穿过元件,不符合,因此需要使用处理计算,判断是否穿过节点本身:

  // 检查两个点组成的线段是否穿过起终点元素
  private checkLineThroughElements(p1: p, p2: p) {
    let rects = [this.Sgraph, this.Egraph];
    let minX = Math.min(p1.x, p2.x);
    let maxX = Math.max(p1.x, p2.x);
    let minY = Math.min(p1.y, p2.y);
    let maxY = Math.max(p1.y, p2.y);

    // 水平线
    if (p1.y === p2.y) {
      for (let i = 0; i < rects.length; i++) {
        let rect = rects[i];
        if (
          minY > rect.getY() - this.ly &&
          minY < rect.getY() + rect.getHeight() - this.ly &&
          minX < rect.getX() + rect.getWidth() - this.lx &&
          maxX > rect.getX() - this.lx
        ) {
          return true;
        }
      }
    } else if (p1.x === p2.x) {
      // 垂直线
      for (let i = 0; i < rects.length; i++) {
        let rect = rects[i];
        if (
          minX > rect.getX() - this.lx &&
          minX < rect.getX() + rect.getWidth() - this.lx &&
          minY < rect.getY() + rect.getHeight() - this.ly &&
          maxY > rect.getY() - this.ly
        ) {
          return true;
        }
      }
    }

    return false;
  }

        推荐大家看一下该博客,我也借鉴了些思路,在点的分析,定位,算法的实现上,都看了他的代码,但是还有些部分需要根据项目实际进行优化的点。关联线探究,如何连接流程图的两个节点

        最终实现效果:

        这部分应该是最难的了,目前实现起来,在临界值的处理上,还是有些问题,包括距离相近时,和优化线的方向问题,还没有做兼容处理。

发布NPM

        包的发布过程就不细说了,大家可以参考网上的教程,如果 npm login 报错,基本上切换淘宝镜像就可以解决问题了。目前经过 mvp 版本的升级迭代,基本具备流程图的绘制、交互功能,我们发布 1.0 版本,主要是测试应用功能模块是否正常。【worker 路径存在问题,目前版本使用同步实现,后续优化】

        当你升级版本的时候报错,一定需要全部提交代码,才可以进行升级:

         版本升级成功,可以在 npm 官网进行查看:

        新建空项目,进行测试:

 

        使用过程中,发现有些静态资源请求地址有问题,我们采取base64编码的方式处理,不走请求,即可解决该问题,整体效果如下:

总结

        目前已经基本实现流程图的图形绘制、自定义icon 、文本输入,剩余的优化问题包括:

1. 折线算法优化;

2. 元件库拓展;

3. 顶部菜单栏及相应 command API开发;

4. 快捷键的完善与相应功能实现。

        大家可以下载包试试,有啥问题随时反馈改进哦。

npm i svg-flow-editor-mvp

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

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

相关文章

flutter 修改app名字和图标

一、修改名字 在Android中修改应用程序名称&#xff1a; 在AndroidManifest.xml文件中修改应用程序名称&#xff1a; 打开Flutter项目中的android/app/src/main/AndroidManifest.xml文件。找到<application>标签&#xff0c;然后在android:label属性中修改应用程序的名称…

Jenkins拉取github项目相关问题

1.私有仓库问题 1.1如果你的仓库是私有的&#xff0c;21年起github就不支持账号密码的方式拉取代码了 那么就需要在github上面创建一个token (classic) 然后在Jenkins代码设置那里 然后应该就可以顺利打包了。 2.找不到pom&#xff08;多了一层文件夹&#xff09;问题 解…

关系型数据库mysql(8)sql高级语句②

目录 一.子查询——Subquery 语法 环境准备 In——查询已知的值的数据记录 子查询——Insert 子查询——Update 子查询——Delete Not In——表示否定&#xff0c;不在子查询的结果集里 Exists——判断查询结果集是否为空 子查询——别名 ​编辑 二.视图 理论&a…

踩坑uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效的问题

首先看到这是uni官网提出的&#xff0c;app上建议使用高德地图。 下面就用高德地图进行配置。 步骤一&#xff1a;登陆高德地图控制台 名称和类型根据自己情况填写选择即可 步骤二&#xff1a; 添加key 步骤三&#xff1a;取到SHA1 进入uniapp开发官网 点击应用名称&#…

如何使用Windows电脑部署Lychee私有图床网站并实现无公网IP远程管理本地图片

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-MSVdVLkQMnY9Y2HW {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

Webpack生成企业站静态页面 - 增强数据处理能力

一些项目因需求不同&#xff0c;如需SEO或小项目&#xff0c;使用angular、react或vue就大材小用了。我们可以通过webpack、gulp这些构建工具&#xff0c;也能快速完成html页面开发&#xff0c;并且也能使用less/sass/styus等样式预编译功能&#xff0c;以及将js、html分模块、…

pyecharts操作三

pyecharts操作三 pyecharts 是一个用于生成Echarts图表的Python库。Echarts是百度开源的一个数据可视化JS库&#xff0c;可以生成一些非常酷炫的图表。 环境安装 pip install pyecharts 检查版本 import pyecharts print(pyecharts.version) 2.0.3 GL关系图 import rando…

Linux(CentOS7)安装 Redis

目录 下载 上传 解压 编译与安装 修改配置文件 ​编辑 启动redis 客户端使用 下载 官网地址&#xff1a; Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Re…

想学网络安全,从哪里开始?网络安全的学习路线

网络安全学习路线&#xff1a; 想学习网络安全专业的知识&#xff0c;想当黑客&#xff0c;但是不知道该从哪里开始学。 我给你一个路线&#xff01; 清晰图片和大纲&#xff1a;https://docs.qq.com/doc/DU1lpVFpSbWVrd2p3

Linux安装redis(基于CentOS系统,Ubuntu也可参考)

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a;wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a;tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…

MySQL进阶-----索引的语法与SQL性能分析

目录 前言 一、索引语法 1.SQL语法 2.案例演示 二、SQL性能分析 三、慢查询日志 1.开启日志 2.测试样例 四、profile详情 1.开启profile 2.profile测试SQL语句 五、explain详情 1.语法结构 2.执行顺序示例&#xff08;id&#xff09; 3.执行性能示例(type) 前言 本…

斜率优化dp 笔记

任务安排1 有 N 个任务排成一个序列在一台机器上等待执行&#xff0c;它们的顺序不得改变。 机器会把这 N 个任务分成若干批&#xff0c;每一批包含连续的若干个任务。 从时刻 00 开始&#xff0c;任务被分批加工&#xff0c;执行第 i 个任务所需的时间是 Ti。 另外&#x…

uniApp使用XR-Frame创建3D场景(6)播放模型动画

上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用 这篇我们讲解播放模型动画 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{{sec6}}"><xr-light type"ambient&qu…

火车头通过关键词采集文章的原理

随着互联网信息的爆炸式增长&#xff0c;网站管理员和内容创作者需要不断更新和发布新的文章&#xff0c;以吸引更多的用户和提升网站的排名。而火车头作为一款智能文章采集工具&#xff0c;在这一过程中发挥着重要作用。本文将探讨火车头如何通过关键词采集文章&#xff0c;以…

<QT基础(5)>事件监听

事件监听 事件监听&#xff08;Event Handling&#xff09;是在程序中监视和响应发生的事件的一种机制。在Qt中&#xff0c;事件监听是一种常见的用于处理用户输入、系统事件以及其他类型事件的方法。通过事件监听&#xff0c;您可以在发生特定事件时捕获事件并执行相应的操作…

HarmonyOS 应用开发之Want的定义与用途

Want 是一种对象&#xff0c;用于在应用组件之间传递信息。 其中&#xff0c;一种常见的使用场景是作为 startAbility() 方法的参数。例如&#xff0c;当UIAbilityA需要启动UIAbilityB并向UIAbilityB传递一些数据时&#xff0c;可以使用Want作为一个载体&#xff0c;将数据传递…

VMware vSAN OSA存储策略 - 基于虚拟机的分布式对象存储

简介 博客&#xff1a;https://songxwn.com/ 存储策略 (Storage Policy) 是管理员定义的一组规则&#xff0c;这组规则定义了数据对象在 vSAN 存储上是如何保存的&#xff0c;存储策略定义了数据存储的可靠性、访问性能等特性。vSAN 提供了基于存储策略的存储管理 SPBM (Stor…

解码“零信任”,如何带来信任感?

零信任的“信任”来源&#xff0c;并非凭空而生&#xff0c;而是建立在严格、细致且持续的验证、策略之上。它不仅能够提升企业的安全防护能力&#xff0c;也在加速安全技术的创新与演进。 推动创新 零信任理念激活网络安全 身份和访问管理革新。零信任理念“永不信任&#…

Jenkins升级中的小问题

文章目录 使用固定版本安装根据jenkins页面下载war包升级jenkins重启jenkins报错问题解决 K8s部署过程中的一些小问题 ##### Jenkins版本小插曲 ​ 在Jenkins环境进行插件安装时全部清一色飘红&#xff0c;发现是因为Jenkins版本过低导致&#xff0c;报错的位置可以找到更新je…

【InternLM 实战营第二期笔记】书生·浦语大模型全链路开源体系及InternLM2技术报告笔记

大模型 大模型成为发展通用人工智能的重要途径 专用模型&#xff1a;针对特定任务&#xff0c;一个模型解决一个问题 通用大模型&#xff1a;一个模型应对多种任务、多种模态 书生浦语大模型开源历程 2023.6.7&#xff1a;InternLM千亿参数语言大模型发布 2023.7.6&#…