咸阳 网站建设,鲜花网网站开发的意义,淘客做网站有必要吗,销售外包系列文章
Svg Flow Editor 原生svg流程图编辑器#xff08;一#xff09;
Svg Flow Editor 原生svg流程图编辑器#xff08;二#xff09;
Svg Flow Editor 原生svg流程图编辑器#xff08;三#xff09;
Svg Flow Editor 原生svg流程图编辑器#xff08;四#xf…系列文章
Svg Flow Editor 原生svg流程图编辑器一
Svg Flow Editor 原生svg流程图编辑器二
Svg Flow Editor 原生svg流程图编辑器三
Svg Flow Editor 原生svg流程图编辑器四
实现Echart统计图 统计图的底层我们采用apache echarts 实现【Apache ECharts】先封装GEchartGEchart是我们的外层框架支持形变、旋转与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 barechart.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 值得变化需要做优化
/*** 需要做位置矫正* classsf-editor-box-graphs 正确* classsf-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 构建 graphconst 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 OFFSETconst 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