首页 » 前端 » Javascript » 正文

为 Grapheditor 添加蚂蚁线

发布者:站点默认
2011/04/26 浏览数(1,501) 分类:Javascript 为 Grapheditor 添加蚂蚁线已关闭评论

20230216更新:这里有添加好蚂蚁线的 GraphEditor。

下载原版

GraphEditor 来自 mxGraph 的文档

下载 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;
  }
}

— 完 —

点击返回顶部
  1. 留言
  2. 联系方式