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

系列文章

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯刷题计划-洛谷-持续更新

P8598 [蓝桥杯 2013 省 AB] 错误票据 题目 #include <bits/stdc.h> #define endl \n #define int long long #define INF 0x3f3f3f3f3f const int N 1000010; using namespace std; int arr[N]; signed main() {int N;cin>>N;int idx;while(cin>>arr[idx…

Element使用

Element使用 一、入门介绍 Element最基本的元素是段&#xff0c;Section&#xff08;Grid&#xff09;&#xff0c;通常一个页面包含多个段&#xff1b;如果要做一个Banner大图&#xff0c;首先得添加一个段&#xff0c;这个段里面的额结构是两个栏&#xff0c;栏里面包含多个…

百度智能云推出AI大模型全家桶;抖音发布 AI 生成虚拟人物治理公告

百度智能云推出大模型全家桶 百度智能云昨日在北京首钢园召开「Al Cloud Day: 大模型应用产品发布会」&#xff0c;此次发布会上&#xff0c;百度智能云宣布对以下 7 款产品进行升级。 数字人平台百度智能云曦灵智能客服平台百度智能云客悦内容创作平台「一念」知识智平台「甄…

Ruoyi-Cloud-Plus_使用Docker部署分布式微服务系统---SpringCloud工作笔记200

1.首先安装docker: 如果以前安装过首先执行: yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 去卸载docker 2.安装dokcer需要的工具包…

StarRocks实战——多点大数据数仓构建

目录 前言 一、背景介绍 二、原有架构的痛点 2.1 技术成本 2.2 开发成本 2.2.1 离线 T1 更新的分析场景 2.2.2 实时更新分析场景 2.2.3 固定维度分析场景 2.2.4 运维成本 三、选择StarRocks的原因 3.1 引擎收敛 3.2 “大宽表”模型替换 3.3 简化Lambda架构 3.4 模…

【有芯职说】数字芯片BES工程师

一、 数字芯片BES工程师简介 今天来聊聊数字芯片BES工程师&#xff0c;其中BES是Back End Support的缩写&#xff0c;就是后端支持的意思。其实这个岗位是数字IC前端设计和数字IC后端设计之间的一座桥&#xff0c;完成从寄存器传输级设计到具体工艺的mapping和实现。这个岗位在…

JetBrains pycharm pro 2023 for mac Python集成开发环境

JetBrains PyCharm Pro 2023 for Mac是一款功能强大的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为Mac用户设计&#xff0c;旨在提供高效、智能的编程体验。 软件下载&#xff1a;JetBrains pycharm pro 2023 for mac中文最新版 PyCharm Pro 2023支持多种语…

HelpLook AI ChatBot:自定义Prompts综合指南

AI问答机器人&#xff08;AI Chatbot&#xff09;日益在各行业普及&#xff0c;但回答准确率的不足仍是其面临的痛点。用户在与AI问答机器人的互动中常发现&#xff0c;机器人难以完全理解和准确回答复杂问题。HelpLook可以通过自定义提示词&#xff08;Prompts&#xff09;和集…

软件杯 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Tensorflow2.0笔记 - 自定义Layer和Model

本笔记主要记录如何在tensorflow中实现自定的Layer和Model。详细内容请参考代码中的链接。 import time import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets, layers, optimizers, Sequential, metricstf.__version__ #关于自定义l…

国自然提交状态,NSFC已审核 ≠ 申请书被受理!!!

本 期 推 荐 【SciencePub学术】2024年度国家自然科学基金集中受理期项目申请受理工作已基本结束。到底什么状态才算申请书被NSFC接收成功呢&#xff1f; 01 申请书状态 申请人登录ISIS系统&#xff0c;至此&#xff0c;绝大部分申请人的系统状态为下面三种&#xff1a; …

排序C++

题目 法1 sort升序排序&#xff0c;再逆序输出 #include<iostream> #include<algorithm> using namespace std;const int N 5e53;//注意const&#xff0c;全局 int a[N]; int main() {//错误int N5e53;//错误const int a[N];int n;cin >> n;for (int i 1;…

激光是如何产生的?

激光产生的原理 美国于1960年成功研制出世界上第一台红宝石激光器&#xff0c;我国也于1961年成功研制出第一台国产红宝石激光器&#xff08;诞生于中国科学院长春光学精密机械研究所&#xff09;&#xff0c;激光技术被认为是第二个20世纪&#xff0c;继量子物理、无线电技术、…

CenOS安装yum(超详细)

专栏文章索引&#xff1a;Linux 目录 1.检查yum源是否安装 2.卸载yum源 3.去网站下载yum源&#xff0c;至少需要下载3个 4.安装&#xff08;不要出现其他后缀名为rpm的文件&#xff09; 1.检查yum源是否安装 rpm -qa|grep yum 2.卸载yum源 查看一下是否成功删除 3.去网站下…

剑指Offer题目笔记20(在数组范围内二分查找)

面试题72&#xff1a; 问题&#xff1a; ​ 输入一个非负整数&#xff0c;计算它的平方根。 解决方案&#xff1a; 使用二分查找。一个数x的平方根一定小于或等于x&#xff0c;同时&#xff0c;除了0之外的所有非负整数的平方根都大于等于1&#xff0c;故该数的平方根在1到x…

php 快速入门(七)

一、操作数据库 1.1 操作MySQL的步骤 第一步&#xff1a;登录MySQL服务器 第二步&#xff1a;选择当前数据库 第三步&#xff1a;设置请求数据的字符集 第四步&#xff1a;执行SQL语句 1.2 连接MySQL 函数1&#xff1a;mysql_connect() 功能&#xff1a;连接&#xff08;登录…

HTTP

HTTP 概念&#xff1a;HyperTextTransferProtocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 HTTP协议特点&#xff1a; 1.基于TCP协议&#xff1a;面向连接&#xff0c;安全 2.基于请求-响应模型的&#xff1a;一次请求对应一次响应 …

pipeline script for SCM 构建go项目

pipeline script 和 pipeline script for SCM 推荐使用第二种 首先需要再项目的根目录下新建Jenkinsfile 文件 pipeline for SCM 拉取github 代码 自动生成Jenkinsfile 的语法 生成jenkinsfile 的拉取脚本 项目中编写Jenkinsfile文件 pipeline {agent anystages …

如何评估户外LED显示屏的质量标准

随着数字媒体的不断进步&#xff0c;户外LED显示屏已经成为现代城市不可或缺的一部分&#xff0c;它们以鲜明的视觉冲击力和广泛的应用范围&#xff0c;成为了广告和公共信息传播的重要工具。然而&#xff0c;并非所有的户外LED显示屏都能满足高标准的户外使用要求。为了确保投…