133

如何在两个或多个元素之间画一条线来连接它们?HTML/CSS/JavaScript/SVG/Canvas 的任意组合都可以。

如果您的答案支持其中任何一项,请务必提及:

  • 可拖动元素
  • 可拖动/可编辑的连接
  • 避免元素重叠

这个问题已经更新以整合它的众多变体。

4

12 回答 12

172

jsPlumb是一个支持拖放的可用选项,从其众多演示中可以看出,包括流程图演示

它提供免费社区版和付费工具包版。

Toolkit 版将 Community 版与全面的数据绑定层以及用于构建应用程序和集成流行库的几个 UI 小部件相结合,并获得商业许可。

于 2011-06-08T18:47:21.613 回答
72

用 svgs 连接线条对我来说值得一试,而且效果很好……首先,可缩放矢量图形 (SVG) 是一种基于 XML 的二维图形矢量图像格式,支持交互性和动画。SVG 图像及其行为在 XML 文本文件中定义。<svg>您可以使用标签在 HTML 中创建 svg 。Adobe Illustrator 是用于使用路径创建复杂 svg 的最佳软件之一。

使用一条线连接两个 div 的过程:

  1. 创建两个 div 并根据需要为它们提供任何位置

    <div id="div1" style="width: 100px; height: 100px; top:0; left:0; background:#e53935 ; position:absolute;"></div>
    <div id="div2" style="width: 100px; height: 100px; top:0; left:300px; background:#4527a0 ; position:absolute;"></div>
    

    (为了解释起见,我正在做一些内联样式,但为样式制作一个单独的 css 文件总是好的)

  2. <svg><line id="line1"/></svg>

    线标签允许我们在两个指定点(x1,y1)和(x2,y2)之间画一条线。(作为参考,请访问 w3schools。)我们尚未指定它们。因为我们将使用 jQuery 来编辑行标签的属性 (x1,y1,x2,y2)。

  3. <script>标签写

    line1 = $('#line1');   
    div1 = $('#div1');   
    div2 = $('#div2');
    

    我使用选择器来选择两个 div 和 line...

    var pos1 = div1.position();
    var pos2 = div2.position();
    

    jQueryposition()方法允许我们获取元素的当前位置。欲了解更多信息,请访问https://api.jquery.com/position/(您也可以使用offset()方法)

现在我们已经获得了我们需要的所有位置,我们可以画线如下......

line1
  .attr('x1', pos1.left)
  .attr('y1', pos1.top)
  .attr('x2', pos2.left)
  .attr('y2', pos2.top);

jQuery.attr()方法用于更改所选元素的属性。

我们在上面的行中所做的只是改变了行的属性

x1 = 0
y1 = 0
x2 = 0
y2 = 0

x1 = pos1.left
y1 = pos1.top
x2 = pos2.left
y2 = pos2.top

因为position()返回两个值,一个'left'和另一个'top',我们可以使用对象(这里是pos1和pos2)使用.top和.left轻松访问它们......

现在线标签有两个不同的坐标来绘制两点之间的线。

提示:根据需要向 div 添加事件侦听器

提示:确保在脚本标签中写入任何内容之前先导入 jQuery 库

通过 JQuery 添加坐标后......它看起来像这样

以下片段仅用于演示目的,请按照上述步骤获得正确的解决方案

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div1" style="width: 100px; height: 100px; top:0; left:0; background:#e53935 ; position:absolute;"></div>
<div id="div2" style="width: 100px; height: 100px; top:0; left:300px; background:#4527a0 ; position:absolute;"></div>
<svg width="500" height="500"><line x1="50" y1="50" x2="350" y2="50" stroke="red"/></svg>

于 2016-02-18T22:29:27.997 回答
41

最近,我尝试开发一个简单的 Web 应用程序,它使用拖放组件并通过线连接它们。我遇到了这两个简单而神奇的 javascript 库:

  1. Plain Draggable:简单且高性能的库,允许拖动 HTML/SVG 元素。
  2. 领导线:在您的网页中绘制领导线

工作示例链接(用法:单击添加场景以创建可拖动对象,单击添加选项以在两个不同的可拖动对象之间绘制引导线)

于 2020-04-28T13:25:48.477 回答
7

几天前我也有同样的要求

我使用了一个完整的宽度高度 svg,并将其添加到我所有的 div 下方,并动态地将线条添加到这些 svg 中。

查看我是如何使用svg在这里完成的

HTML

<div id="ui-browser"><div class="anchor"></div>
     <div id="control-library" class="library">
       <div class="name-title">Control Library</div>
       <ul>
         <li>Control A</li>
         <li>Control B</li>
         <li>Control C</li>
         <li>Control D</li>
       </ul>
     </div><!--
--></div><!--
--><div id="canvas">
     <svg id='connector_canvas'></svg>
     <div class="ui-item item-1"><div class="con_anchor"></div></div>
     <div class="ui-item item-2"><div class="con_anchor"></div></div>
     <div class="ui-item item-3"><div class="con_anchor"></div></div>
     <div class="ui-item item-1"><div class="con_anchor"></div></div>
     <div class="ui-item item-2"><div class="con_anchor"></div></div>
     <div class="ui-item item-3"><div class="con_anchor"></div></div>
   </div><!--
--><div id="property-browser"></div>

https://jsfiddle.net/kgfamo4b/

    $('.anchor').on('click',function(){
   var width = parseInt($(this).parent().css('width'));
   if(width==10){
     $(this).parent().css('width','20%');
     $('#canvas').css('width','60%');
   }else{
      $(this).parent().css('width','10px');
     $('#canvas').css('width','calc( 80% - 10px)');
   }
});

$('.ui-item').draggable({
  drag: function( event, ui ) {
           var lines = $(this).data('lines');
           var con_item =$(this).data('connected-item');
           var con_lines = $(this).data('connected-lines');

           if(lines) {
             lines.forEach(function(line,id){
                    $(line).attr('x1',$(this).position().left).attr('y1',$(this).position().top+1);  
             }.bind(this));
           }

           if(con_lines){
               con_lines.forEach(function(con_line,id){
                  $(con_line).attr('x2',$(this).position().left)
                        .attr('y2',$(this).position().top+(parseInt($(this).css('height'))/2)+(id*5));
               }.bind(this));

           }

       }
});

$('.ui-item').droppable({
  accept: '.con_anchor',
  drop: function(event,ui){
     var item = ui.draggable.closest('.ui-item');
     $(this).data('connected-item',item);
     ui.draggable.css({top:-2,left:-2});
     item.data('lines').push(item.data('line'));

     if($(this).data('connected-lines')){
        $(this).data('connected-lines').push(item.data('line'));

         var y2_ = parseInt(item.data('line').attr('y2'));
         item.data('line').attr('y2',y2_+$(this).data('connected-lines').length*5);

     }else $(this).data('connected-lines',[item.data('line')]);

     item.data('line',null);
    console.log('dropped');
  }
});


$('.con_anchor').draggable({drag: function( event, ui ) {
     var _end = $(event.target).parent().position();
     var end = $(event.target).position();
     if(_end&&end)  
     $(event.target).parent().data('line')
                                                    .attr('x2',end.left+_end.left+5).attr('y2',end.top+_end.top+2);
},stop: function(event,ui) {
        if(!ui.helper.closest('.ui-item').data('line')) return;
        ui.helper.css({top:-2,left:-2});
        ui.helper.closest('.ui-item').data('line').remove();
        ui.helper.closest('.ui-item').data('line',null);
        console.log('stopped');
      }
});


$('.con_anchor').on('mousedown',function(e){
    var cur_ui_item = $(this).closest('.ui-item');
    var connector = $('#connector_canvas');
    var cur_con;

  if(!$(cur_ui_item).data('lines')) $(cur_ui_item).data('lines',[]);

  if(!$(cur_ui_item).data('line')){
         cur_con = $(document.createElementNS('http://www.w3.org/2000/svg','line'));
         cur_ui_item.data('line',cur_con);
    } else cur_con = cur_ui_item.data('line');

    connector.append(cur_con);
    var start = cur_ui_item.position();
     cur_con.attr('x1',start.left).attr('y1',start.top+1);
     cur_con.attr('x2',start.left+1).attr('y2',start.top+1);
});
于 2017-04-25T11:29:59.310 回答
3

VisJS通过其箭头示例支持这一点,该示例支持可拖动元素。

它还通过交互事件示例支持可编辑的连接。

于 2018-11-05T21:53:49.327 回答
2

GoJS通过其状态图示例支持这一点,支持拖放元素和编辑连接。

此答案基于Walter Northwoods 的 答案

于 2018-11-05T21:31:28.610 回答
1

Raphaël通过其Graffle 示例支持这一点。

该答案基于Awais Akhtar 的 答案Vaibhav Jain 的 答案

于 2018-11-05T21:10:16.760 回答
1

mxGraph - 由draw.io使用 - 支持这个用例,它的Grapheditor 示例文档。 例子。

该答案基于Vainbhav Jain 的 答案

于 2018-11-05T21:43:58.250 回答
1

Cytoscape通过其支持拖动元素的架构示例来支持这一点。

为了创建连接,有edgehandles扩展。它还不支持编辑现有连接。 问题。

对于编辑连接形状,有边缘编辑扩展。演示。

编辑编辑扩展似乎很有希望,但是还没有演示。

于 2018-11-05T21:51:15.970 回答
1

js-graph.it支持这个用例,正如它的入门指南所见,支持拖动元素而没有连接重叠。它似乎不支持编辑/创建连接。好像不再维护了。

于 2018-11-05T22:48:15.317 回答
0

JointJS/Rappid通过其Kitchensink 示例支持此用例,该示例支持元素的拖放和连接的重新定位。它有很多例子。

该答案基于Vainbhav Jain 的 答案

于 2018-11-05T21:36:25.070 回答