基于@antv/x6的流程图

基于@antv/x6的流程图

1.引言

基于 antv/x6 实现自定义流程图。

实现的功能有:背景网格编辑、链接桩隐藏与显示、适应画布、小地图、放大、缩小、层级改变、复制、剪切、粘贴、全选、框选、撤销、重做、重置、删除、清空、数据保存、另存为图片以及一些快捷键设置

antv/x6 官方文档:X6 图编辑引擎 | AntV

2.实际效果

gitee源码: 流程图-x6
在这里插入图片描述

3.下载与插件

3.1 下载x6

npm i @antv/x6@2.17.1

3.2 其他x6插件

		"@antv/x6": "^2.17.1",
		"@antv/x6-plugin-clipboard": "^2.0.0", // 如果使用剪切板功能,需要安装此包
		"@antv/x6-plugin-dnd": "^2.0.0",// 如果使用 dnd 功能,需要安装此包
		"@antv/x6-plugin-export": "^2.1.6",// 如果使用图片导出功能,需要安装此包
		"@antv/x6-plugin-history": "^2.0.0",// 如果使用撤销重做功能,需要安装此包
		"@antv/x6-plugin-keyboard": "^2.0.0", // 如果使用快捷键功能,需要安装此包
		"@antv/x6-plugin-minimap": "^2.0.0",// 如果使用小地图功能,需要安装此包
		"@antv/x6-plugin-scroller": "^2.0.0",// 如果使用滚动画布功能,需要安装此包
		"@antv/x6-plugin-selection": "^2.0.0", // 如果使用框选功能,需要安装此包
		"@antv/x6-plugin-snapline": "^2.0.0",// 如果使用对齐线功能,需要安装此包
		"@antv/x6-plugin-stencil": "^2.0.0",// 如果使用 stencil 功能,需要安装此包
		"@antv/x6-plugin-transform": "^2.0.0",// 如果使用图形变换功能,需要安装此包
		"@antv/x6-react-components": "^2.0.0", // 如果使用配套 UI 组件,需要安装此包
		"@antv/x6-react-shape": "^2.0.0", // 如果使用 react 渲染功能,需要安装此包
		"@antv/x6-vue-shape": "^2.0.0",// 如果使用 vue 渲染功能,需要安装此包				      	      "@vue/composition-api": "^1.7.2",// 如果使用 vue 渲染功能自定义节点时,在 vue2 下还需要安装此包,

注意:使用 @antv/x6-vue-shape 自定义节点时 在 vue2 下还需要安装 @vue/composition-api ,否则会报错
在这里插入图片描述

官方文档:使用 HTML/React/Vue/Angular 渲染 | X6

4.基础使用

4.1 插件引入

import { register, getTeleport } from "@antv/x6-vue-shape/lib";// register注册外部节点
import { Graph, Shape, Path, Edge, Platform, DataUri } from "@antv/x6";
import { Transform } from "@antv/x6-plugin-transform";
import { Selection } from "@antv/x6-plugin-selection";
import { Snapline } from "@antv/x6-plugin-snapline";
import { Keyboard } from "@antv/x6-plugin-keyboard";
import { Clipboard } from "@antv/x6-plugin-clipboard";
import { History } from "@antv/x6-plugin-history";
import { Scroller } from "@antv/x6-plugin-scroller";
import { Dnd } from "@antv/x6-plugin-dnd";
import { MiniMap } from "@antv/x6-plugin-minimap";
import { Export } from "@antv/x6-plugin-export";

4.2 注册插件

handleUsePlugin() {
    this.graph
        .use(
        new Transform({
            resizing: true,
            rotating: true,
        })
    )
        .use(
        //点选/框选,开启后可以通过点击或者套索框选节点
        new Selection({
            enabled: true, //启用
            rubberband: false, //是否启用框选
            showNodeSelectionBox: true, //是否显示节点的选择框
        })
    )
    // .use(
    // 	new Scroller({
    // 		enabled: true,
    // 		autoResize: true,
    // 		pannable: true, //是否启用画布平移能力// modifiers: ["alt", "ctrl", "meta"], //设置修饰键后需要点击鼠标并按下修饰键才能触发画布拖拽
    // 		eventTypes: ["leftMouseDown"],
    // 	})
    // )
        .use(new Snapline())
        .use(new Keyboard())
        .use(new Clipboard())
        .use(new History())
        .use(new Export());
},

4.3 初始化渲染画布

initGraph() {
    // 事件交互对象:cell 节点/边 -- node 节点	-- edge 边 -- blank 画布空白区域
    // 创建 Graph 对象,并引入基本设置
    const graph = new Graph({
        container: document.getElementById("wrapper"),
        ...configSetting(Shape),
    });
    this.graph = graph;
    // 画布键盘事件绑定
    this.handleUsePlugin();
    graphBindKey(this.graph);
    // 画布事件 - 鼠标进入节点
    this.graph.on("node:mouseenter", ({ node }) => {
        this.changePortsShow(true, node);
    });
    //鼠标离开节点
    this.graph.on("node:mouseleave", ({ node }) => {
        if (this.isPortsShow) return; //如果链接桩常显状态开启,直接return
        this.changePortsShow(false, node);
    });
    // 点击空白网格
    this.graph.on("blank:click", () => {
        // this.editGrid();
    });
    // 点击编辑节点/边
    this.graph.on("cell:click", ({ cell }) => {
        this.editForm(cell);
        if (this.isPortsShow) return; //如果链接桩常显状态开启,直接return
        this.changePortsShow(false, cell);
    });

    // 历史改变
    this.graph.on("history:change", () => {
        this.canRedo = this.graph.canRedo();
        this.canUndo = this.graph.canUndo();
    });
    // 画布有变化
    graph.on("cell:changed", () => {
        this.isChangeValue();
    });
    // 删除
    this.graph.bindKey(["delete", "backspace"], () => {
        this.handlerDel();
    });
    // 赋值
    // 返现方法
    if (this.value && this.value.length) {
        const resArr = this.value;
        // 导出的时候删除了链接桩设置加回来
        const portsGroups = configNodePorts().groups;
        if (resArr.length) {
            const jsonTemp = resArr.map((item) => {
                if (item.ports) item.ports.groups = portsGroups;
                return item;
            });
            graph.fromJSON(jsonTemp);
        }
    }
    // 指定画布拖拽区
    this.dnd = new Dnd({
        target: this.graph,
        scaled: false,
        dndContainer: document.querySelector(".antv-menu"),
        // 确保拖拽节点时和拖拽完成后节点id一致
        getDragNode: (node) => node.clone({ keepId: true }),
        getDropNode: (node) => {
            // 拖拽结束后
            let data = node.getData();
            // this.menuItem = {
            // 	...data,
            // 	id: node.id,
            // 	isCreate: true,
            // 	showDrawer: true,
            // };
            // console.log("this.menuItem :>> ", this.menuItem);
            return node.clone({ keepId: true });
        },
    });

    this.graph.zoomTo(1);
    this.changeGrid();

    this.graph.enablePanning();
},

4.4 给x6画布绑定事件(实现复制、粘贴、删除、撤销、回复等功能)


		// 链接桩的显示与隐藏,主要是照顾菱形
		changePortsShow(val, node) {
			const container = document.getElementById("wrapper");
			let ports = [];
			if (node) {
				ports = container.querySelectorAll(
					`g[data-cell-id="${node.id}"] .x6-port-body`
				);
			} else {
				ports = container.querySelectorAll(".x6-port-body");
			}
			for (let i = 0, len = ports.length; i < len; i = i + 1) {
				ports[i].style.visibility = val ? "visible" : "hidden";
			}
		},
		// 适应画布
		changeAdaptiveCanvas() {
			// 缩放画布内容,使画布内容充满视口
			this.graph.zoomToFit({ padding: 10, maxScale: 2 });
			this.graph.centerContent(); // 将画布中元素居中展示

			this.zoom = this.graph.zoom();
		},
		// 实际尺寸
		changeActualSize() {
			this.graph.zoomTo(1);
			this.zoom = this.graph.zoom();
		},
		// 小地图的显示与隐藏
		changeMinMapShow() {
			// minMapShow -- true(显示)-- false(隐藏)
			this.minMapShow = !this.minMapShow;
			if (this.minMapShow) {
				// 如果小地图的实例显示,重新注册
				this.graph.use(
					new MiniMap({
						container: document.getElementById("minimap"),
						width: 200,
						height: 160,
						scalable: false,
						minScale: 0.1,
						maxScale: 2,
						padding: 10,
					})
				);
			} else {
				// 移除创建
				this.graph.disposePlugins("minimap");
			}
		},
		// 放大
		EnlargeRoom() {
			const zoom = this.graph.zoom().toFixed(1); //获取画布缩放比例
			if (zoom < 2) {
				this.graph.zoom(0.1);
				this.zoom = this.graph.zoom().toFixed(1);
			}
		},
		// 缩小
		ShrinkRoom() {
			const zoom = this.graph.zoom().toFixed(1); //获取画布缩放比例
			if (zoom > 0.5) {
				this.graph.zoom(-0.1);
				this.zoom = this.graph.zoom().toFixed(1);
			}
		},
		// 向下一层
		toBackLevel() {
			const cells = this.graph.getSelectedCells(); //获取选中的节点/边
			if (cells.length) {
				this.selectCell.toBack();
			}
		},
		// 向上一层
		toFrontLevel() {
			const cells = this.graph.getSelectedCells(); //获取选中的节点/边
			if (cells.length) {
				this.selectCell.toFront();
			}
		},
		// 复制
		copyCells() {
			const cells = this.graph.getSelectedCells(); //获取选中的节点/边
			if (cells.length) {
				this.graph.copy(cells); //复制节点/边
			} else {
				this.$message({ type: "info", message: "请先选中节点再复制" });
			}
		},
		// 剪切
		cutCells() {
			const cells = this.graph.getSelectedCells(); //获取选中的节点/边
			if (cells.length) {
				this.graph.cut(cells); //剪切节点/边
			} else {
				this.$message({ type: "info", message: "请先选中节点再剪切" });
			}
		},
		// 粘贴
		selectCells() {
			if (!this.graph.isClipboardEmpty()) {
				//返回剪切板是否为空
				const cells = this.graph.paste({ offset: 30, useLocalStorage: true }); //粘贴,返回粘贴到画布的节点/边。
				this.graph.cleanSelection(); //清空选区。
				this.graph.select(cells); //选中指定的节点/边
			} else {
				this.$message({ type: "info", message: "剪切板为空,不可粘贴!" });
			}
		},
		// 全选
		selectAllNodes() {
			const nodes = this.graph.getNodes(); //返回画布中所有节点
			if (nodes) {
				this.graph.select(nodes); //选中指定的节点/边
			}
		},
		// 撤销
		handleUndo() {
			if (this.graph.canUndo()) {
				//是否可以撤销
				this.graph.undo(); //撤销
			}
		}, 
		// 重做
		handleRedo() {
			if (this.graph.canRedo()) {
				//是否可以重做
				this.graph.redo(); //重做
			}
		},
		// 重置
		handlerReset() {
			this.isValueChange();
		},
		// 清空
		handlerClear() {
			this.graph.clearCells(); //清空画布
		},
		// 框选
		handlerSelection() {
			if (this.graph.isRubberbandEnabled()) {
				this.graph.disableRubberband();
				this.graph.enablePanning();
			} else {
				this.graph.enableRubberband();
				this.graph.disablePanning();
			}
		},
		// 网格参数设置
		changeGridType(e) {
			this.grid.type = e;
			if (this.grid.type === "doubleMesh") {
				this.grid.args = [
					{
						color: "#ccc", // 主网格线颜色
						thickness: 1, //  主网格线宽度/网格点大小
					},
					{
						color: "#999", // 次网格线颜色
						thickness: 1, // 次网格线宽度
						factor: 4, // 主次网格线间隔
					},
				];
			} else {
				this.grid.args = {
					color: "#ccc", // 主网格线颜色
					thickness: 1, //  主网格线宽度/网格点大小
				};
			}
			this.changeGrid();
		},
		// 网格绘制
		changeGrid() {
			this.graph.drawGrid({
				...this.grid,
			});
		},

4.5 编辑节点/边

//点击编辑节点/边
		editForm(cell) {
			console.log("obje点击编辑节点/边ct");
			if (this.selectCell) this.selectCell.removeTools(); //删除所有工具
			this.selectCell = cell; //当前选中或点击的节点/边
			// 编辑node节点
			if (
				cell.isNode() &&
				cell.data.type &&
				cell.data.type.includes("default")
			) {
				this.editTitle = "编辑节点"; //节点/边参数编辑名称
				const body =
					cell.attrs.body ||
					cell.attrs.rect ||
					cell.attrs.polygon ||
					cell.attrs.circle;
				//节点参数
				this.form = {
					labelText: cell.attrs.label.text || "", //节点文本
					fontSize: cell.attrs.label.fontSize || 14, //字体大小
					fontFill: cell.attrs.label.fill || "", //字体颜色
					fill: body.fill || "", //节点背景
					stroke: body.stroke || "", //边框颜色
				};
				//侧边栏编辑抽屉显现
				return (this.editDrawer = true);
			}
			// 编辑图片节点
			if (cell.isNode() && cell.data.type && cell.data.type === "otherImage") {
				this.editTitle = "编辑图片节点";
				const attrs = cell.attrs || {
					body: { fill: "" },
					label: { text: "", fill: "" },
					image: { xlinkHref: "", height: 80, width: 80 },
				};
				//节点参数
				this.form = {
					labelText: attrs.label.text, //节点文本
					labelFill: attrs.label.fill, //字体颜色
					fill: attrs.body.fill, //节点背景
					height: (attrs.image && attrs.image.height) || 80, //图片尺寸
					width: (attrs.image && attrs.image.width) || 80, //图片尺寸
					//图片地址
					xlinkHref:
						(attrs.xlinkHref && attrs.image.xlinkHref) ||
						"https://copyright.bdstatic.com/vcg/creative/cc9c744cf9f7c864889c563cbdeddce6.jpg@h_1280",
				};
				//侧边栏编辑抽屉显现
				return (this.editDrawer = true);
			}
			// 编辑线
			if (!cell.isNode() && cell.shape === "edge") {
				this.editTitle = "编辑连线";
				//边参数
				this.form = {
					label:
						cell.labels && cell.labels[0]
							? cell.labels[0].attrs.labelText.text
							: "",
					stroke: cell.attrs.line.stroke || "",
					connector: "normal",
					strokeWidth: cell.attrs.line.strokeWidth || "",
					isArrows: cell.attrs.line.sourceMarker ? true : false, //sourceMarker - 起始箭头
					isAnit: cell.attrs.line.strokeDasharray ? true : false, //流动线条
					isTools: false, //调整线条
				};
				// 看是否有label
				const edgeCellLabel =
					(cell.labels && cell.labels[0] && cell.labels[0].attrs) || false;
				if (this.form.label && edgeCellLabel) {
					//标签内容参数
					this.labelForm = {
						fontColor: edgeCellLabel.labelText.fill || "#333", //字体颜色
						fill: edgeCellLabel.labelBody.fill || "#fff", //背景颜色
						stroke: edgeCellLabel.labelBody.stroke || "#555", //描边颜色
					};
				} else {
					this.labelForm = { fontColor: "#333", fill: "#FFF", stroke: "#333" };
				}
				//侧边栏编辑抽屉显现
				return (this.editDrawer = true);
			}
		},
		// 隐藏侧边栏编辑抽屉
		closeEditForm() {
			this.editDrawer = false;
			if (this.selectCell) this.selectCell.removeTools(); //删除所有工具
		},
		// 修改一般节点
		changeNode(type, value) {
			switch (type) {
				case "labelText":
					this.selectCell.attr("label/text", value);
					break;
				case "fontSize":
					this.selectCell.attr("label/fontSize", value);
					break;
				case "fontFill":
					this.selectCell.attr("label/fill", value);
					break;
				case "fill":
					this.selectCell.attr("body/fill", value);
					break;
				case "stroke":
					this.selectCell.attr("body/stroke", value);
					break;
			}
		},
		// 修改图片节点
		changeImageNode(type, value) {
			switch (type) {
				case "labelText":
					this.selectCell.attr("label/text", value);
					break;
				case "labelFill":
					this.selectCell.attr("label/fill", value);
					break;
				case "fill":
					this.selectCell.attr("body/fill", value);
					break;
				case "xlinkHref":
					this.selectCell.attr("image/xlinkHref", value);
					break;
				case "height":
					this.selectCell.attr("image/height", value);
					break;
				case "width":
					this.selectCell.attr("image/width", value);
					break;
			}
		},
		// 修改边label属性
		changeEdgeLabel(label, fontColor, fill, stroke) {
			this.selectCell.setLabels([
				configEdgeLabel(label, fontColor, fill, stroke),
			]);
			if (!label)
				this.labelForm = { fontColor: "#333", fill: "#FFF", stroke: "#555" };
		},
		// 修改边的颜色
		changeEdgeStroke(val) {
			this.selectCell.attr("line/stroke", val);
		},
		// 边的样式
		changeEdgeConnector(val) {
			switch (val) {
				case "normal":
					this.selectCell.setConnector(val);
					break;
				case "smooth":
					this.selectCell.setConnector(val);
					break;
				case "rounded":
					this.selectCell.setConnector(val, { radius: 20 });
					break;
				case "jumpover":
					this.selectCell.setConnector(val, { radius: 20 });
					break;
			}
		},
		// 边的宽度
		changeEdgeStrokeWidth(val) {
			if (this.form.isArrows) {
				this.selectCell.attr({
					line: {
						strokeWidth: val,
						sourceMarker: {
							width: 12 * (val / 2) || 12,
							height: 8 * (val / 2) || 8,
						},
						targetMarker: {
							width: 12 * (val / 2) || 12,
							height: 8 * (val / 2) || 8,
						},
					},
				});
			} else {
				this.selectCell.attr({
					line: {
						strokeWidth: val,
						targetMarker: {
							width: 12 * (val / 2) || 12,
							height: 8 * (val / 2) || 8,
						},
					},
				});
			}
		},
		// 边的箭头
		changeEdgeArrows(val) {
			if (val) {
				this.selectCell.attr({
					line: {
						sourceMarker: {
							name: "block",
							width: 12 * (this.form.strokeWidth / 2) || 12,
							height: 8 * (this.form.strokeWidth / 2) || 8,
						},
						targetMarker: {
							name: "block",
							width: 12 * (this.form.strokeWidth / 2) || 12,
							height: 8 * (this.form.strokeWidth / 2) || 8,
						},
					},
				});
			} else {
				this.selectCell.attr({
					line: {
						sourceMarker: "",
						targetMarker: {
							name: "block",
							size: 10 * (this.form.strokeWidth / 2) || 10,
						},
					},
				});
			}
		},
		// 边的添加蚂蚁线
		changeEdgeAnit(val) {
			if (val) {
				this.selectCell.attr({
					line: {
						strokeDasharray: 5,
						style: {
							animation: "ant-line 30s infinite linear",
						},
					},
				});
			} else {
				this.selectCell.attr({
					line: {
						strokeDasharray: 0,
						style: {
							animation: "",
						},
					},
				});
			}
		},
		// 给线添加调节工具
		changeEdgeTools(val) {
			if (val) this.selectCell.addTools(["vertices", "segments"]);
			else this.selectCell.removeTools();
		},
		// 删除节点
		handlerDel() {
			this.$confirm(
				`此操作将永久删除此${this.editTitle === "编辑节点" || this.editTitle === "编辑图片节点"
					? "节点"
					: "连线"
				}, 是否继续?`,
				"提示",
				{
					confirmButtonText: "确定",
					cancelButtonText: "取消",
					type: "warning",
				}
			)
				.then(() => {
					const cells = this.graph.getSelectedCells(); //获取选中的节点/边
					if (cells.length) {
						this.graph.removeCells(cells);
						this.form = {};
						this.editDrawer = false;
						this.selectCell = "";
						this.$message({ type: "success", message: "删除成功!" });
					}
				})
				.catch(() => { });
		},

4.6 导出、另存为图片


		// 导出
		handlerSend() {
			// 我在这里删除了链接桩的设置,和工具(为了减少数据),反显的时候要把删除的链接桩加回来
			const { cells: jsonArr } = this.graph.toJSON();
			const tempGroupJson = jsonArr.map((item) => {
				if (item.ports && item.ports.groups) delete item.ports.groups;
				if (item.tools) delete item.tools;
				return item;
			});
			if (this.selectCell) {
				this.selectCell.removeTools();
				this.selectCell = "";
			}
			this.$emit("finish", JSON.stringify(tempGroupJson));
		},
		//另存为图片
		handlerSendPng() {
			this.graph.toPNG(
				(dataUri) => {
					// 下载
					DataUri.downloadDataUri(dataUri, "chart.png");
				},
				{
					copyStyles: false,
					padding: {
						top: 20,
						right: 30,
						bottom: 40,
						left: 50,
					},
				}
			);
		},

4.7 基本设置

import {
	configSetting,
	configNodeShape,
	configNodePorts,
	configEdgeLabel,
	graphBindKey,
} from "./antvSetting";
// 基本设置
export const configSetting = (Shape) => {
	return {
		background: {
			color: '#fff',
		},
		// scroller: true,
		// grid: true, //网格,默认使用 10px 的网格,但不绘制网格背景
		pannable: {
			enabled: true,
			modifiers: ["alt", "ctrl", "meta"], //设置修饰键后需要点击鼠标并按下修饰键才能触发画布拖拽
			eventTypes: ['leftMouseDown']
		},
		autoResize: true,//是否监听容器大小改变,并自动更新画布大小。
		// translating: { restrict: true },//限制节点移动
		mousewheel: {//鼠标滚轮缩放
			enabled: false,//是否开启滚轮缩放交互
			zoomAtMousePosition: true,//是否将鼠标位置作为中心缩放
			modifiers: 'ctrl',//修饰键,设置修饰键后需要按下修饰键并滚动鼠标滚轮时才触发画布缩放
			minScale: 0.5,//最小的缩放级别
			maxScale: 2,//最大的缩放级别
		},
		connecting: {//连线选项
			router: {//路由
				//name: 'orth', orth 正交路由,由水平或垂直的正交线段组成。
				name: 'manhattan',//	智能正交路由,由水平或垂直的正交线段组成,并自动避开路径上的其他节点(障碍)。
				args: {//路由参数
					padding: 10,//设置锚点距离转角的最小距离
				},
			},
			connector: {//连接器
				name: 'normal',//圆角连接器时
				args: {//连接器参数
					radius: 10,//倒角半径
				},
			},
			highlight: true,
			anchor: 'center',//当连接到节点时,指定被连接的节点的锚点
			connectionPoint: 'anchor',//指定连接点
			allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 true
			allowBlank: false,//是否允许连接到画布空白位置的点
			snap: {//是否触发自动吸附,当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附
				radius: 20,//自定义触发吸附的距离
			},
			highlight: true,
			createEdge() {//连接的过程中创建新的边
				return new Shape.Edge({
					attrs: {//定制样式
						line: {//定义了 'line'选择器
							stroke: '#000',// 使用自定义边框色
							strokeWidth: 2, //边宽度
							targetMarker: {//终点箭头
								name: 'block',//箭头名:实心箭头
								width: 12,//箭头宽度
								height: 8//	箭头高度
							},
						},
					},
					zIndex: 0,//层级
				})
			},
			validateConnection({ targetMagnet }) {//在移动边的时候判断连接是否有效
				return !!targetMagnet
			},
		},
		onToolItemCreated({ tool }) {//当工具项渲染完成时触发的回调
			const handle = tool
			const options = handle.options
			if (options && options.index % 2 === 1) {
				tool.setAttrs({ fill: 'red' })
			}
		},
		highlighting: {//高亮选项,指定触发某种交互时的高亮样式
			// 当链接桩可以被链接时,在链接桩外围渲染一个 2px 宽的红色矩形框
			magnetAvailable: {
				name: 'stroke',
				args: {
					padding: 1,
					attrs: {
						'stroke-width': 1,
						fill: '#fff',//填充的颜色
						stroke: '#b1e1fa', //边的颜色
					}
				}
			},
			magnetAdsorbed: {//连线过程中,自动吸附到链接桩时被使用
				name: 'stroke',
				args: {//高亮样式
					attrs: {//定制样式
						padding: 4,
						'stroke-width': 4,
						fill: '#fff',//填充的颜色
						stroke: '#5F95FF', //边的颜色
					},
				},
			},
		},
		resizing: true,//缩放节点
		rotating: true,//旋转节点
		// selecting: {//点选/框选,开启后可以通过点击或者套索框选节点
		// 	enabled: true,//启用
		// 	rubberband: true,//是否启用框选
		// 	showNodeSelectionBox: true,//是否显示节点的选择框
		// },
		snapline: true,//对齐线
		keyboard: true,//键盘快捷键
		clipboard: true,//剪切板
		history: true,
	}
}

// 节点预设类型 (0椭圆形: defaultOval, 1方形: defaultSquare, 2圆角矩形: defaultYSquare, 3菱形: defaultRhombus, 4平行四边形: defaultRhomboid, 5圆形: defaultCircle, 6图片: otherImage)
export const configNodeShape = (type) => {
	const nodeShapeList = [{
		label: '椭圆形',
		/**
		 *  加入data里面的标识type是为了方便编辑的时候找到相对应的类型进行不同的编辑处理
		 *  另外获取初始对应的设置
		*/
		data: {
			type: 'defaultOval'
		},
		shape: 'rect',
		width: 100,
		height: 50,
		attrs: {
			// 指定 rect 元素的样式
			body: {
				rx: 20,// x 半径
				ry: 26,//y 半径
				fill: '#fff',// 填充颜色
				stroke: '#333'// 边框颜色
			},
			// 指定 text 元素的样式
			label: {
				text: '椭圆形', // 文本
				fontSize: 16,// 文字大小
				fill: '#333'//文字颜色
			},
		}
	},
	{
		label: '方形',
		data: {
			type: 'defaultSquare',
		},
		shape: 'rect',
		width: 100,
		height: 50,
		attrs: {
			label: {
				text: '方形',
				fontSize: 16,
				fill: '#333'
			},
			body: {
				fill: '#fff',
				stroke: '#333'
			}
		},
	},
	{
		label: '圆角矩形',
		data: {
			type: 'defaultYSquare'
		},
		shape: 'rect',
		width: 100,
		height: 50,
		attrs: {
			body: {
				rx: 6,
				ry: 6,
				fill: '#fff',
				stroke: '#333'
			},
			label: {
				text: '圆角矩形',
				fontSize: 16,
				fill: '#333'
			}
		},
	},
	{
		label: '菱形',
		data: {
			type: 'defaultRhombus'
		},
		shape: 'polygon',
		width: 120,
		height: 50,
		attrs: {
			body: {
				refPoints: '0,10 10,0 20,10 10,20',
				fill: '#fff',
				stroke: '#333'
			},
			label: {
				text: '菱形',
				fontSize: 16,
				fill: '#333'
			}
		},
	},
	{
		label: '平行四边形',
		data: {
			type: 'defaultRhomboid'
		},
		shape: 'polygon',
		width: 120,
		height: 50,
		attrs: {
			body: {
				refPoints: '10,0 40,0 30,20 0,20',
				fill: '#fff',
				stroke: '#333'
			},
			label: {
				text: '平行四边形',
				fontSize: 16,
				fill: '#333'
			}
		}
	},
	{
		label: '圆形',
		data: {
			type: 'defaultCircle'
		},
		shape: 'circle',
		width: 80,
		height: 80,
		attrs: {
			label: {
				text: '圆形',
				fontSize: 16,
				fill: '#333'
			},
			body: {
				fill: '#fff',
				stroke: '#333'
			}
		}
	},
	{
		label: "图片",
		data: {
			type: 'otherImage'
		},
		shape: 'rect',
		width: 80,
		height: 80,
		markup: [
			{
				tagName: 'rect',
				selector: 'body',
			},
			{
				tagName: 'image',
			},
			{
				tagName: 'text',
				selector: 'label',
			},
		],
		attrs: {
			body: {
				fill: '#fff',
				stroke: '#333'
			},
			image: {
				width: 80,
				height: 80,
				refX: 0,
				refY: 0,
				xlinkHref: 'https://pic4.zhimg.com/80/v2-369ccdc73ae7e715bd1e4093072d3e3b_720w.jpg',
			},
			label: {
				fontSize: 14,
				fill: '#333',
				text: '图片'
			},
		},
	}
	]
	if (type) {
		const obj = nodeShapeList.find(item => { return item.data.type === type })
		return obj || nodeShapeList
	}
	return nodeShapeList
}

// 节点连接装设置
export const configNodePorts = () => {
	return {
		groups: {//链接桩组定义
			top: {
				position: 'top',//链接桩位置
				attrs: {//属性和样式
					circle: {//圆形
						r: 4,//圆半径
						magnet: true,//使链接桩在连线交互时可以被连接上
						stroke: '#5F95FF',//自定义边框色
						strokeWidth: 1,//边宽度
						fill: '#fff',//填充的颜色
						style: {
							visibility: 'hidden',//是否可见
						},
					},
				},
			},
			right: {
				position: 'right',
				attrs: {
					circle: {
						r: 4,
						magnet: true,
						stroke: '#5F95FF',
						strokeWidth: 1,
						fill: '#fff',
						style: {
							visibility: 'hidden',
						},
					},
				},
			},
			bottom: {
				position: 'bottom',
				attrs: {
					circle: {
						r: 4,
						magnet: true,
						stroke: '#5F95FF',
						strokeWidth: 1,
						fill: '#fff',
						style: {
							visibility: 'hidden',
						},
					},
				},
			},
			left: {
				position: 'left',
				attrs: {
					circle: {
						r: 4,
						magnet: true,
						stroke: '#5F95FF',
						strokeWidth: 1,
						fill: '#fff',
						style: {
							visibility: 'hidden',
						},
					},
				},
			},
		},
		items: [
			{
				group: 'top',
			},
			{
				group: 'right',
			},
			{
				group: 'bottom',
			},
			{
				group: 'left',
			},
		]
	}
}

// 连线 label 设置
export const configEdgeLabel = (labelText, fontColor, fill, stroke) => {
	if (!labelText) return { attrs: { labelText: { text: '' }, labelBody: { fill: '', stroke: '' } } }
	return {
		markup: [//定制标签样式
			{
				tagName: 'rect',
				selector: 'labelBody',
			},
			{
				tagName: 'text',
				selector: 'labelText',
			},
		],
		attrs: {//标签样式
			labelText: {
				text: labelText || '',
				fill: fontColor || '#333',
				textAnchor: 'middle',
				textVerticalAnchor: 'middle',
			},
			labelBody: {
				ref: 'labelText',
				refX: -8,
				refY: -5,
				refWidth: '100%',
				refHeight: '100%',
				refWidth2: 16,
				refHeight2: 10,
				stroke: stroke || '#555',
				fill: fill || '#fff',
				strokeWidth: 2,
				rx: 5,
				ry: 5,
			},
		}
	}
}

// 键盘事件
export const graphBindKey = (graph) => {
	// bindKey - 绑定键盘快捷键
	// 复制
	graph.bindKey(['meta+c', 'ctrl+c'], () => {
		const cells = graph.getSelectedCells()//获取选中的节点/边
		if (cells.length) {
			graph.copy(cells)//复制节点/边
		}
		return false
	})
	// 剪切
	graph.bindKey(['meta+x', 'ctrl+x'], () => {
		const cells = graph.getSelectedCells()//获取选中的节点/边
		if (cells.length) {
			graph.cut(cells)//剪切节点/边
		}
		return false
	})
	// 粘贴
	graph.bindKey(['meta+v', 'ctrl+v'], () => {
		if (!graph.isClipboardEmpty()) {//返回剪切板是否为空
			const cells = graph.paste({ offset: 32 })//粘贴,返回粘贴到画布的节点/边。
			graph.cleanSelection()//清空选区。
			graph.select(cells)//选中指定的节点/边
		}
		return false
	})
	//撤销
	graph.bindKey(['meta+z', 'ctrl+z'], () => {
		console.log('graph.canUndo() :>> ', graph.canUndo());
		if (graph.canUndo()) {//是否可以撤销
			graph.undo()//撤销
		}
		return false
	})
	// 重做
	graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
		console.log('graph.canRedo() :>> ', graph.canRedo());
		if (graph.canRedo()) {//是否可以重做
			graph.redo()//重做
		}
		return false
	})
	// 全选
	// graph.bindKey(['meta+a', 'ctrl+a'], () => {
	// 	const nodes = graph.getNodes()//返回画布中所有节点
	// 	if (nodes) {
	// 		graph.select(nodes)//选中指定的节点/边
	// 	}
	// })
	//删除 - 后续组件单独特殊处理
	// graph.bindKey('backspace', () => {
	// 	const cells = graph.getSelectedCells()
	// 	if (cells.length) {
	// 		graph.removeCells(cells)
	// 	}
	// })
	// 缩放
	// graph.bindKey(['ctrl+1', 'meta+1'], () => {
	// 	const zoom = graph.zoom()//获取画布缩放比例
	// 	if (zoom < 1.5) {
	// 		graph.zoom(0.1)
	// 	}
	// })
	// graph.bindKey(['ctrl+2', 'meta+2'], () => {
	// 	const zoom = graph.zoom()//获取画布缩放比例
	// 	if (zoom > 0.5) {
	// 		graph.zoom(-0.1)
	// 	}
	// })
	return graph
}


  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值