在画布上寻找自定义(加入 div 并维护它的关系)

Har*_*dia 2 javascript canvas relationship visual-editor html5-canvas

我正在寻找一个 javascript 库或自定义解决方案,我可以在其中自由拖放组件并维护它们之间的关系(例如哪个节点连接到什么以及在我想要的任何地方自由移动节点)

通过维护关系,我的意思是不同的组件应该维护它们的互连流(如流程图)。绘制它们后,我需要获取它们关系的 JSON 数据。

下面是我在说什么的示例一瞥

我开发的 Strom React Diagrams

在上图中,您可以看到我有不同的节点相互连接。如何通过库或自定义解决方案实现这些目标?

上图来自strom-react-diagrmas反应库。我试过这个,但它使用 SVG 并且缺乏我想要的很多自定义。

我也试过rete.js但无法根据我的需要自定义它(自定义形状等)。

网路js

我也在考虑从头开始构建解决方案,我面临的唯一问题是如何在画布上加入两个或多个 div 以保持其关系?

注意我为什么要这样做?

  1. 我这样做的目标是创建一个可视化编辑器,非技术人员可以在其中设计流程,然后我想导出 JSON 以将其相应地存储在我的数据库中。
  2. 当我再次加载此流程的画布时,我应该能够根据我将拥有的 JSON 数据再次呈现互连流程以及连接的节点。

如果你遇到这样的情况,你能给我一些建议吗?非常感谢你们的任何帮助。

enx*_*eta 5

我很想知道更多关于你心目中的布局。

这是一个演示,您可以在其中单击灰点。单击 2 个点时,将在 svg 画布上绘制 2 个点之间的连接。

在 HTML 中,您将所有元素都放在一个#wrap元素中。在 div 下方有一个 svg 元素,大小与#wrap. divabsolute使用 top 和 left 属性按百分比定位。svg 画布有一个viewBox="0 0 100 100"preserveAspectRatio="none"以便使绘图适应#wrap. 连接器是在 svg 上绘制的路径,在拉伸或压扁的画布上使用fill:nonevector-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)
}
Run Code Online (Sandbox Code Playgroud)
* {
  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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


Ni5*_*5aN 5

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;
    }
...
Run Code Online (Sandbox Code Playgroud)

模板:

  <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>
Run Code Online (Sandbox Code Playgroud)

因此,您可以不受限制地自定义节点、连接和背景