Поиск настроек на холсте (присоединение к div и поддержание их отношений) - PullRequest
0 голосов
/ 19 февраля 2019

Я ищу библиотеку javascript или нестандартное решение, в котором я могу свободно перетаскивать перетаскиваемые компоненты и поддерживать связь между ними (например, какой узел подключен к чему и свободно перемещать узлы, куда я хочу)

Под поддержанием взаимосвязи я подразумеваю, что различные компоненты должны поддерживать свой поток взаимосвязи ( как блок-схема ).После их рисования мне нужно получить данные JSON об их отношениях.

Ниже приведен пример того, о чем я говорю

Strom React Diagrams Which I developed

На изображении выше, как вы можете видеть, у меня есть разные узлы, которые связаны между собой.Как я могу достичь этих целей с помощью библиотеки или специального решения?

Изображение выше из библиотеки strom-Reaction-diagrmas реагирует.Я пробовал это, но он использует SVG и не хватает многих настроек, которые мне нужны.

Я также пытался rete.js , но не смог настроить его в соответствии со своими потребностями (настройка формтак далее).

Rete js

Я также думаю о создании решения с нуля, единственная проблема, с которой я сталкиваюсь, это как мне соединить два или несколько делений наcanvas поддерживает свои отношения?

Заметьте, почему я это делаю?

  1. Моя цель заключается в том, чтобы я хотел создать визуальный редактор, в которомтехнический специалист может спроектировать поток, а затем я хочу экспортировать JSON, чтобы соответствующим образом сохранить его в моей базе данных.
  2. Когда я снова загружу холст этого потока, я смогу отобразить поток взаимосвязи вместе со связанными узламиопять же на основе данных JSON, которые у меня будут.

Можете ли вы мне что-то предложить, если вы столкнулись с такой ситуацией?Любая помощь от вас, ребята, очень ценится.

Ответы [ 3 ]

0 голосов
/ 21 февраля 2019

Вы можете использовать GOJS .

Это отличное решение для коммерческого проекта.Он гибок в настройках и позволяет довольно просто делать удивительные вещи.

Пример на официальном сайте .

function init() {
  if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
  var $ = go.GraphObject.make; // for conciseness in defining templates

  myDiagram =
    $(go.Diagram, "myDiagramDiv", {
      validCycle: go.Diagram.CycleNotDirected, // don't allow loops
      // For this sample, automatically show the state of the diagram's model on the page
      "undoManager.isEnabled": true
    });

  // This template is a Panel that is used to represent each item in a Panel.itemArray.
  // The Panel is data bound to the item object.
  var fieldTemplate =
    $(go.Panel, "TableRow", // this Panel is a row in the containing Table
      new go.Binding("portId", "name"), // this Panel is a "port"
      {
        background: "transparent", // so this port's background can be picked by the mouse
        fromSpot: go.Spot.Right, // links only go from the right side to the left side
        toSpot: go.Spot.Left,
        // allow drawing links from or to this port:
        fromLinkable: true,
        toLinkable: true
      },
      $(go.Shape, {
          width: 12,
          height: 12,
          column: 0,
          strokeWidth: 2,
          margin: 4,
          // but disallow drawing links from or to this shape:
          fromLinkable: false,
          toLinkable: false
        },
        new go.Binding("figure", "figure"),
        new go.Binding("fill", "color")),
      $(go.TextBlock, {
          margin: new go.Margin(0, 5),
          column: 1,
          font: "bold 13px sans-serif",
          alignment: go.Spot.Left,
          // and disallow drawing links from or to this text:
          fromLinkable: false,
          toLinkable: false
        },
        new go.Binding("text", "name")),
      $(go.TextBlock, {
          margin: new go.Margin(0, 5),
          column: 2,
          font: "13px sans-serif",
          alignment: go.Spot.Left
        },
        new go.Binding("text", "info"))
    );

  // This template represents a whole "record".
  myDiagram.nodeTemplate =
    $(go.Node, "Auto", {
        copyable: false,
        deletable: false
      },
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      // this rectangular shape surrounds the content of the node
      $(go.Shape, {
        fill: "#EEEEEE"
      }),
      // the content consists of a header and a list of items
      $(go.Panel, "Vertical",
        // this is the header for the whole node
        $(go.Panel, "Auto", {
            stretch: go.GraphObject.Horizontal
          }, // as wide as the whole node
          $(go.Shape, {
            fill: "#1570A6",
            stroke: null
          }),
          $(go.TextBlock, {
              alignment: go.Spot.Center,
              margin: 3,
              stroke: "white",
              textAlign: "center",
              font: "bold 12pt sans-serif"
            },
            new go.Binding("text", "key"))),
        // this Panel holds a Panel for each item object in the itemArray;
        // each item Panel is defined by the itemTemplate to be a TableRow in this Table
        $(go.Panel, "Table", {
            padding: 2,
            minSize: new go.Size(100, 10),
            defaultStretch: go.GraphObject.Horizontal,
            itemTemplate: fieldTemplate
          },
          new go.Binding("itemArray", "fields")
        ) // end Table Panel of items
      ) // end Vertical Panel
    ); // end Node

  myDiagram.linkTemplate =
    $(go.Link, {
        relinkableFrom: true,
        relinkableTo: true, // let user reconnect links
        toShortLength: 4,
        fromShortLength: 2
      },
      $(go.Shape, {
        strokeWidth: 1.5
      }),
      $(go.Shape, {
        toArrow: "Standard",
        stroke: null
      })
    );

  myDiagram.model =
    $(go.GraphLinksModel, {
      copiesArrays: true,
      copiesArrayObjects: true,
      linkFromPortIdProperty: "fromPort",
      linkToPortIdProperty: "toPort",
      nodeDataArray: [{
          key: "Record1",
          fields: [{
              name: "field1",
              info: "",
              color: "#F7B84B",
              figure: "Ellipse"
            },
            {
              name: "field2",
              info: "the second one",
              color: "#F25022",
              figure: "Ellipse"
            },
            {
              name: "fieldThree",
              info: "3rd",
              color: "#00BCF2"
            }
          ],
          loc: "0 0"
        },
        {
          key: "Record2",
          fields: [{
              name: "fieldA",
              info: "",
              color: "#FFB900",
              figure: "Diamond"
            },
            {
              name: "fieldB",
              info: "",
              color: "#F25022",
              figure: "Rectangle"
            },
            {
              name: "fieldC",
              info: "",
              color: "#7FBA00",
              figure: "Diamond"
            },
            {
              name: "fieldD",
              info: "fourth",
              color: "#00BCF2",
              figure: "Rectangle"
            }
          ],
          loc: "280 0"
        }
      ],
      linkDataArray: [{
          from: "Record1",
          fromPort: "field1",
          to: "Record2",
          toPort: "fieldA"
        },
        {
          from: "Record1",
          fromPort: "field2",
          to: "Record2",
          toPort: "fieldD"
        },
        {
          from: "Record1",
          fromPort: "fieldThree",
          to: "Record2",
          toPort: "fieldB"
        }
      ]
    });
}

init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/2.0.3/go.js"></script>
<div id="sample">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px"></div>
</div>
0 голосов
/ 24 февраля 2019

Rete.js можно настроить с помощью пользовательских компонентов Vue.js.

Пример

Визуальная часть платформы представлена ​​однимиз плагинов для рендеринга: vue или stage0.Я предпочитаю Vue.js, поэтому я разработал плагин на его основе.

Создание собственного сокета и узла


var CustomSocket = {
  template: `<div class="socket"
    :class="[type, socket.name, used()?'used':''] | kebab"
    :title="socket.name+'\\n'+socket.hint"></div>`,
  props: ['type', 'socket', 'used']
}


var CustomNode = {
  template,
  mixins: [VueRenderPlugin.mixin],
  methods:{
    used(io){
      return io.connections.length;
    }
  },
  components: {
    Socket: /*VueRenderPlugin.Socket*/CustomSocket
  }
}


class NumComponent extends Rete.Component {

    constructor(){
        super("Number");
        this.data.component = CustomNode;
    }
...

Шаблон:

  <div class="node" :class="[selected(), node.name] | kebab">
  <div class="title">{{node.name}}</div>
  <div class="content">
    <div class="col" v-if="node.controls.size&gt;0 || node.inputs.size&gt;0">
      <div class="input" v-for="input in inputs()" :key="input.key" style="text-align: left">
        <Socket v-socket:input="input" type="input" :socket="input.socket" :used="() => input.connections.length"></Socket>
        <div class="input-title" v-show="!input.showControl()">{{input.name}}</div>
        <div class="input-control" v-show="input.showControl()" v-control="input.control"></div>
     </div>
     <div class="control" v-for="control in controls()" v-control="control"></div>
    </div>
    <div class="col">
      <div class="output" v-for="output in outputs()" :key="output.key">
        <div class="output-title">{{output.name}}</div>
        <Socket v-socket:output="output" type="output" :socket="output.socket" :used="() => output.connections.length"></Socket>
      </div>
    </div>
  </div> 
</div>

КакВ результате вы можете настроить узлы, соединения и фон без ограничений

0 голосов
/ 20 февраля 2019

Мне бы хотелось узнать больше о макете, который вы имеете в виду.

Это демо, где вы можете щелкнуть серые точки.Когда щелкают 2 точки, соединение между двумя точками рисуется на холсте svg.

В HTML все ваши элементы находятся внутри элемента #wrap.Под элементами div находится элемент svg того же размера, что и #wrap.Div расположены absolute с верхним и левым атрибутами в процентах.У холста svg есть viewBox="0 0 100 100" и preserveAspectRatio="none", чтобы адаптировать рисунок к размеру #wrap.Соединители - это пути, нарисованные в svg с помощью fill:none и vector-effect: non-scaling-stroke; для равномерного обводки растянутого или сжатого холста.

В конце вы можете сохранить массив точек для данных.

Надеюсь, это может дать вам представление о том, что вам нужно делать.

const SVG_NS = 'http://www.w3.org/2000/svg';
let mainBox = wrap.getBoundingClientRect();

let dots = Array.from(document.querySelectorAll(".dot"))

let points = [];
let count = 0;

dots.forEach(d=>{
  d.addEventListener("click",(e)=>{
    
    let bcr = d.getBoundingClientRect();
    mainBox = wrap.getBoundingClientRect()
    // calculate the x and y coordinates for the connectors as a number from 0 to 100 
    let x = map(bcr.left - mainBox.left + bcr.width/2, mainBox.left, mainBox.left + mainBox.width, 0, 100);
    let y = map(bcr.top - mainBox.top + bcr.height/2, mainBox.top, mainBox.top + mainBox.height, 0, 100);
    
    points.push({x,y})
    if(count % 2 == 1){
      // connects the last 2 dots in the array
      drawConnector(points[points.length-1],points[points.length-2])
    }    
    count++;
  })
})

function map(n, a, b, _a, _b) {
  let d = b - a;
  let _d = _b - _a;
  let u = _d / d;
  return _a + n * u;
}


function drawConnector(a,b){
  let path = document.createElementNS(SVG_NS, 'path');
  let d = `M${a.x},${a.y} C50,${a.y} 50 ${b.y} ${b.x} ${b.y}`;
  path.setAttributeNS(null,"d",d);
  svg.appendChild(path)
}
* {
  box-sizing: border-box;
}
.box {
  width: 20%;
  height: 100px;
  border: 1px solid #bbb;
  border-radius: 10px;
  position: absolute;
  background: #efefef;
}

#wrap {
  position: absolute;
  margin:auto;
  top:0;bottom:0;left:0;right:0;
  width: 60%;
  height: 350px;
  border: 1px solid;
  min-width: 350px;
}

svg {
  position: absolute;
  width: 100%;
  height: 100%;
  background: rgba(0, 100, 250, 0.25);
}

.dot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid #999;
  background: #d9d9d9;
  position: relative;
  left: calc(100% - 10px);
}

.dot:hover {
  border-color: tomato;
}

path {
  fill: none;
  stroke: black;
  vector-effect: non-scaling-stroke;
  stroke-width: 1px;
  stroke: #555;
}
<div id="wrap">
<svg id="svg" viewBox="0 0 100 100" preserveAspectRatio="none"></svg>  
  
<div class="box" id="a" style="top: 10%; left: 10%;">
  <div class="dot" style="top:20px" ></div>
  <div class="dot" style="top:40px" ></div>
</div>
<div class="box" id="b" style="top: 60%; left: 10%;">
  <div class="dot" style="top:20px" ></div>
  <div class="dot" style="top:40px" ></div>
</div>
<div class="box"  id="c" style="top: 30%; left: 65%; ">
  <div class="dot" style="top:20px; left:-10px" ></div>
  <div class="dot" style="top:40px; left:-10px" ></div>  
</div>
  
</div>
...