为 Grapheditor 添加蚂蚁线
20230216更新:这里有添加好蚂蚁线的 GraphEditor。
下载原版
下载 GraphEditor:下载代码后,目录 javascript/examples/grapheditor/www 里即是完整的 GraphEditor。
为原版 GraphEditor 添加蚂蚁线(流水效果)
grapheditor/resources/grapheditor.txt 添加3行高亮部分:
flipH=Flip Horizontal flipV=Flip Vertical flowingLine=Ant Line reverseFlow=Flow Reverse flow=Flow
grapheditor/js/EditorUi.js 添加高亮处的2行:
var connectStyles = [ "flowingLineStyle", "flowingLineBackgroundColor", "shape", "edgeStyle", "curved",
grapheditor/js/Graph.js 添加高亮行处的代码:
// 连线的动画 Graph.flowingLineEnabled = true; /** * Default size for line jumps. */ Graph.defaultJumpSize = 6;
grapheditor/js/Format.js 这个文件需要添加共 4 处。
1/4 添加高亮行:
Format.prototype.initSelectionState = function() {
return {
flowingLine: true,
vertices: [],
edges: [],
2/4 添加高亮行:
this.container.appendChild(this.addLineJumps(this.createPanel())); this.container.appendChild(this.addFlowingLine(this.createPanel()));
3/4 在文件末尾添加以下代码:
/**
* Adds UI for configuring line animations.
*/
StyleFormatPanel.prototype.addFlowingLine = function (container)
{
var ss = this.format.getSelectionState();
if (Graph.flowingLineEnabled && ss.edges.length > 0 && ss.vertices.length == 0 && ss.flowingLine) {
container.style.padding = "8px 0px 24px 18px";
var ui = this.editorUi;
var editor = ui.editor;
var graph = editor.graph;
var span = document.createElement("div");
span.style.position = "absolute";
span.style.fontWeight = "bold";
span.style.width = "80px";
mxUtils.write(span, mxResources.get("flowingLine"));
container.appendChild(span);
var styleSelect = document.createElement("select");
styleSelect.style.position = "absolute";
styleSelect.style.marginTop = "-2px";
styleSelect.style.right = "76px";
styleSelect.style.width = "62px";
var styles = ["none", "flow", "reverseFlow"];
for (var i = 0; i < styles.length; i++) {
var styleOption = document.createElement("option");
styleOption.setAttribute("value", styles[i]);
mxUtils.write(styleOption, mxResources.get(styles[i]));
styleSelect.appendChild(styleOption);
}
graph.addListener("size", function () {
// Adds animation to edge shape and makes "pipe" visible
graph.view.states.visit(function (key, state) {
if (graph.model.isEdge(state.cell)) {
var flowingLineStyle = state.style.flowingLineStyle;
if (flowingLineStyle) {
// console.log('state.style', state.style);
var strokeWidth = state.shape.node.getElementsByTagName("path")[1].getAttribute("stroke-width") || state.style.strokeWidth; // 缩放画布时蚂蚁线背景与线保持同宽
state.shape.node.getElementsByTagName("path")[0].removeAttribute("visibility");
state.shape.node.getElementsByTagName("path")[0].setAttribute("stroke-width", strokeWidth);
state.shape.node.getElementsByTagName("path")[0].setAttribute("stroke", state.style.flowingLineBackgroundColor || "transparent");
state.shape.node.getElementsByTagName("path")[1].setAttribute("class", flowingLineStyle);
// 缩放视图的同时也缩放虚线间隔
if (flowingLineStyle == 'flow') {
// JS 操作 @keyframes: https://developer.mozilla.org/zh-CN/docs/Web/API/Element/animate
state.shape.node.getElementsByTagName("path")[1].animate([
{
strokeDasharray: strokeWidth * 4,
strokeDashoffset: 0,
},
{
strokeDasharray: strokeWidth * 4,
strokeDashoffset: -strokeWidth * 8, // strokeDashoffset = strokeDasharray * 2
}
], {
iterations: Infinity,
duration: 500,
easing: 'linear',
});
}
}
}
});
});
mxEvent.addListener(styleSelect, "change", function (evt) {
graph.getModel().beginUpdate();
try {
graph.setCellStyles("flowingLineStyle", styleSelect.value, graph.getSelectionCells());
ui.fireEvent(
new mxEventObject(
"styleChanged",
"keys",
["flowingLineStyle"],
"values",
[styleSelect.value],
"cells",
graph.getSelectionCells()
)
);
} finally {
graph.getModel().endUpdate();
}
mxEvent.consume(evt);
});
// Stops events from bubbling to color option event handler
mxEvent.addListener(styleSelect, "click", function (evt) {
mxEvent.consume(evt);
});
container.appendChild(styleSelect);
var option = this.createColorPicker(
function () {
var state = graph.view.getState(graph.getSelectionCell());
if (state != null) {
return mxUtils.getValue(state.style, 'flowingLineBackgroundColor', null);
}
return null;
},
function (color) {
graph.setCellStyles(
'flowingLineBackgroundColor',
color,
graph.getSelectionCells()
);
},
"transparent",
{
install: function (apply) {
// ignore
},
destroy: function () {
// ignore
},
}
);
option.style.position = "absolute";
option.style.marginTop = "-4px";
option.style.right = "20px";
container.appendChild(option);
var listener = mxUtils.bind(this, function (sender, evt, force) {
ss = this.format.getSelectionState();
styleSelect.value = mxUtils.getValue(ss.style, "flowingLineStyle", "none");
});
graph.getModel().addListener(mxEvent.CHANGE, listener);
this.listeners.push({
destroy: function () {
graph.getModel().removeListener(listener);
},
});
listener();
} else {
container.style.display = "none";
}
return container;
};
4/4 在文件末尾添加以下代码:
BaseFormatPanel.prototype.createColorPicker = function (
getColorFn,
setColorFn,
defaultColor,
listener,
callbackFn
) {
var div = document.createElement("div");
div.style.display = "inline-block";
var value = getColorFn();
var applying = false;
var btn = null;
var apply = function (color, disableUpdate, forceUpdate) {
if (!applying) {
applying = true;
color = /(^#?[a-zA-Z0-9]*$)/.test(color) ? color : defaultColor;
btn.innerHTML =
'<div style="width:' +
(mxClient.IS_QUIRKS ? "30" : "36") +
"px;height:12px;margin:3px;border:1px solid black;background-color:" +
mxUtils.htmlEntities(color != null && color != mxConstants.NONE ? color : defaultColor) +
';"></div>';
// Fine-tuning in Firefox, quirks mode and IE8 standards
if (mxClient.IS_QUIRKS || document.documentMode == 8) {
btn.firstChild.style.margin = "0px";
}
if (callbackFn != null) {
callbackFn(color);
}
if (!disableUpdate) {
value = color;
// Checks if the color value needs to be updated in the model
if (forceUpdate || getColorFn() != value) {
setColorFn(value);
}
}
applying = false;
}
};
btn = mxUtils.button(
"",
mxUtils.bind(this, function (evt) {
this.editorUi.pickColor(value, function (color) {
apply(color, null, true);
});
mxEvent.consume(evt);
})
);
btn.className = "geColorBtn";
div.appendChild(btn);
apply(value, true);
if (listener != null) {
listener.install(apply);
this.listeners.push(listener);
}
return div;
};
grapheditor/styles/grapheditor.css 添加到文件末尾:
path.flow {
stroke-dasharray: 10;
animation: flow 0.5s linear;
animation-iteration-count: infinite;
}
@keyframes flow {
to {
stroke-dashoffset: -20;
}
}
path.reverseFlow {
stroke-dasharray: 10;
animation: reverse-flow 0.5s linear;
animation-iteration-count: infinite;
}
@keyframes reverse-flow {
to {
stroke-dashoffset: 20;
}
}
— 完 —
