Desenhar uma linha de conexão entre dois elementos [fechado]

106

Como posso desenhar uma linha entre dois ou mais elementos para conectá-los? Qualquer combinação de HTML / CSS / JavaScript / SVG / Canvas é adequada.

Se sua resposta apoiar algum destes, então mencione:

  • elementos arrastáveis
  • conexões arrastáveis ​​/ editáveis
  • evitação de sobreposição de elemento

Esta questão foi atualizada para consolidar as inúmeras variações dela.

Bakhtiyor
fonte

Respostas:

164

jsPlumb é uma opção disponível que suporta arrastar e soltar, como pode ser visto em suas inúmeras demos , incluindo a demo Flowchart .

Ele está disponível em uma edição gratuita da comunidade e em uma edição paga do Toolkit.

A edição Toolkit envolve a edição Community com uma camada de vinculação de dados abrangente, bem como vários widgets de UI para construir aplicativos e integrações para bibliotecas populares, e é licenciada comercialmente.

Tomasz Kowalczyk
fonte
4
Kit de ferramentas incrível, mas esteja avisado: não é grátis! Eles querem que você compre uma licença se você pretende hospedar publicamente ou vender seus próprios produtos (consulte jsplumbtoolkit.com/purchase ).
Chris
50

Juntar linhas com svgs valeu a pena para mim, e funcionou perfeitamente ... primeiro de tudo, Scalable Vector Graphics (SVG) é um formato de imagem vetorial baseado em XML para gráficos bidimensionais com suporte para interatividade e animação. As imagens SVG e seus comportamentos são definidos em arquivos de texto XML. você pode criar um svg em HTML usando <svg>tag. Adobe Illustrator é um dos melhores softwares usados ​​para criar um svgs complexo usando caminhos.

Procedimento para unir dois divs usando uma linha:

  1. crie dois divs e dê a eles qualquer posição que você precisar

    <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>

    (para fins de explicação, estou fazendo alguns estilos embutidos, mas é sempre bom fazer um arquivo css separado para estilos)

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

    A etiqueta de linha nos permite desenhar uma linha entre dois pontos especificados (x1, y1) e (x2, y2). (para uma referência, visite w3schools.) não os especificamos ainda. porque usaremos o jQuery para editar os atributos (x1, y1, x2, y2) da tag de linha.

  3. na <script>tag escrever

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

    Usei seletores para selecionar os dois divs e linha ...

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

    O position()método jQuery nos permite obter a posição atual de um elemento. Para obter mais informações, visite https://api.jquery.com/position/ (você também pode usar o offset()método)

Agora, como obtivemos todas as posições de que precisamos, podemos traçar a linha da seguinte forma ...

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

O .attr()método jQuery é usado para alterar os atributos do elemento selecionado.

Tudo o que fizemos na linha acima foi mudamos os atributos da linha de

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

para

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

como position()retorna dois valores, um 'left' e outro 'top', podemos acessá-los facilmente usando .top e .left usando os objetos (aqui pos1 e pos2) ...

Agora a etiqueta de linha tem duas coordenadas distintas para traçar uma linha entre dois pontos.

Dica: adicione ouvintes de eventos conforme necessário para divs

Dica: certifique-se de importar a biblioteca jQuery antes de escrever qualquer coisa na tag de script

Depois de adicionar as coordenadas através do JQuery ... Será algo parecido com isto

O snippet a seguir é apenas para fins de demonstração, siga as etapas acima para obter a solução correta

<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>

Ani
fonte
3
Não copie e cole a mesma resposta para várias perguntas. Em vez disso, personalize as respostas às perguntas individuais.
Andy
2
Eu preciso colocar o svg com largura e altura 100% no fundo usando z-index -1, mas funciona perfeitamente.
steven
4
Esta resposta foi copiada de stackoverflow.com/questions/19382872/…
Damjan Pavlica
31
Quem votou contra você, por favor, comente o motivo disso ... Eu postei as mesmas respostas para ambas as perguntas porque tenho certeza de que essa resposta se aplica a ambas as perguntas ... Se duas perguntas forem relevantes, então suas respostas também podem ser relevantes. . Eu não fiz nada incorreto ...
Ani
6

Eu também tive a mesma exigência alguns dias atrás

Eu usei um SVG de largura e altura total e o adicionei abaixo de todos os meus divs e adicionei linhas a esses SVG dinamicamente.

Veja como fiz aqui usando 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);
});
Nadir Laskar
fonte
Não parece funcionar no Safari versão 12.0.1 (14606.2.104.1.1)
balupton
2

Recentemente, tentei desenvolver um aplicativo da web simples que usa componentes de arrastar e soltar e tem linhas conectando-os. Encontrei duas bibliotecas javascript simples e incríveis:

  1. Simples arrastável : biblioteca simples e de alto desempenho que permite arrastar elementos HTML / SVG.
  2. Linha de liderança : desenhe uma linha de liderança em sua página da web
Querubim
fonte
1

D3 tem centenas de exemplos , alguns dos quais adequados para esta questão.

Exemplos sem arrastar e soltar, que são fixos:

Exemplos sem arrastar e soltar, que são interativos:

Exemplos de arrastar e soltar:

Essa resposta é baseado fora de Glenn Dayton resposta .

Balupton
fonte
1

js-graph.it suporta este caso de uso, conforme visto em seu guia de introdução , suportando arrastar elementos sem sobreposições de conexão. Não parece que ele suporta edição / criação de conexões. Não parece mais ser mantido.

Balupton
fonte