Rysowanie linii łączącej dwa elementy [zamknięte]

106

Jak narysować linię między dwoma lub więcej elementami, aby je połączyć? Dowolna kombinacja HTML / CSS / JavaScript / SVG / Canvas jest w porządku.

Jeśli Twoja odpowiedź jest zgodna z którymkolwiek z powyższych, wspomnij o tym:

  • elementy przeciągane
  • przeciągane / edytowalne połączenia
  • unikanie nakładania się elementów

To pytanie zostało zaktualizowane, aby skonsolidować liczne jego odmiany .

Bakhtiyor
źródło

Odpowiedzi:

164

jsPlumb to dostępna opcja obsługująca przeciąganie i upuszczanie, o czym świadczą liczne wersje demonstracyjne , w tym demo Flowchart .

Jest dostępny w bezpłatnej edycji Community oraz płatnej wersji Toolkit.

Edycja Toolkit obejmuje edycję Community kompleksową warstwą wiązania danych, a także kilkoma widżetami interfejsu użytkownika do tworzenia aplikacji i integracji dla popularnych bibliotek i jest objęta licencją komercyjną.

Tomasz Kowalczyk
źródło
4
Niesamowity zestaw narzędzi, ale uwaga: nie jest darmowy! Chcą, abyś kupił licencję, jeśli zamierzasz udostępniać go publicznie lub sprzedawać w ramach własnych produktów (patrz jsplumbtoolkit.com/purchase ).
Chris,
50

Łączenie linii za pomocą svg było dla mnie warte strzału i zadziałało idealnie ... po pierwsze, Scalable Vector Graphics (SVG) to oparty na XML format obrazu wektorowego dla grafiki dwuwymiarowej z obsługą interaktywności i animacji. Obrazy SVG i ich zachowanie są zdefiniowane w plikach tekstowych XML. możesz stworzyć svg w HTML za pomocą <svg>tagu. Adobe Illustrator to jedno z najlepszych programów służących do tworzenia złożonych plików SVG przy użyciu ścieżek.

Procedura łączenia dwóch elementów div za pomocą linii:

  1. utwórz dwa elementy div i nadaj im dowolną pozycję, jaką potrzebujesz

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

    (ze względu na wyjaśnienie robię pewne style w wierszu, ale zawsze dobrze jest utworzyć osobny plik css do stylizacji)

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

    Znacznik linii pozwala nam narysować linię między dwoma określonymi punktami (x1, y1) i (x2, y2). (w celach informacyjnych odwiedź w3schools). Nie określiliśmy ich jeszcze. ponieważ będziemy używać jQuery do edycji atrybutów (x1, y1, x2, y2) znacznika linii.

  3. w <script>tagu pisać

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

    Użyłem selektorów do wybrania dwóch elementów div i linii ...

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

    position()Metoda jQuery pozwala na uzyskanie aktualnej pozycji elementu. Aby uzyskać więcej informacji, odwiedź https://api.jquery.com/position/ (możesz też użyć offset()metody)

Teraz, gdy otrzymaliśmy wszystkie potrzebne pozycje, możemy narysować linię w następujący sposób ...

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

.attr()Metoda jQuery służy do zmiany atrybutów wybranego elementu.

Wszystko, co zrobiliśmy w powyższej linii, to zmieniliśmy atrybuty linii z

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

do

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

as position()zwraca dwie wartości, jedną 'left', a drugą 'top', możemy łatwo uzyskać do nich dostęp używając .top i .left używając obiektów (tutaj pos1 i pos2) ...

Teraz znacznik linii ma dwie różne współrzędne, aby narysować linię między dwoma punktami.

Wskazówka: dodawaj detektory wydarzeń zgodnie z potrzebami do elementów div

Wskazówka: upewnij się, że najpierw zaimportujesz bibliotekę jQuery przed napisaniem czegokolwiek w tagu script

Po dodaniu współrzędnych przez JQuery ... Będzie to wyglądało mniej więcej tak

Poniższy fragment służy wyłącznie do celów demonstracyjnych. Wykonaj powyższe kroki, aby uzyskać prawidłowe rozwiązanie

<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
źródło
3
Nie kopiuj i nie wklejaj tej samej odpowiedzi na wiele pytań. Zamiast tego dostosuj odpowiedzi na poszczególne pytania.
Andy
2
Muszę umieścić svg na szerokości i wysokości 100% w tle za pomocą z-index -1, ale działa jak urok.
Steven
4
Ta odpowiedź została skopiowana ze stackoverflow.com/questions/19382872/…
Damjan Pavlica
31
Osoby osłabiające głosy, proszę o komentarz, dlaczego to robią… Opublikowałem te same odpowiedzi na oba pytania, ponieważ jestem pewien, że ta odpowiedź ma zastosowanie do obu pytań… Jeśli dwa pytania są istotne, wtedy ich odpowiedzi mogą być również istotne. , Nie zrobiłem nic złego ...
Ani,
6

Miałem te same wymagania kilka dni temu

Użyłem pliku SVG o pełnej szerokości i wysokości i dodałem go poniżej wszystkich moich elementów div i dynamicznie dodałem wiersze do tych plików SVG.

Sprawdź, jak to zrobiłem, używając 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
źródło
Wydaje się, że nie działa w przeglądarce Safari w wersji 12.0.1 (14606.2.104.1.1)
balupton
2

Ostatnio próbowałem stworzyć prostą aplikację internetową, która wykorzystuje komponenty przeciągnij i upuść i ma linie łączące je. Natknąłem się na te dwie proste i niesamowite biblioteki javascript:

  1. Plain Draggable : prosta i wydajna biblioteka umożliwiająca przeciąganie elementu HTML / SVG.
  2. Linia odniesienia : narysuj linię odniesienia na swojej stronie internetowej
Cherubini
źródło
1

D3 ma setki przykładów , z których niektóre są odpowiednie dla tego pytania.

Przykłady bez przeciągania i upuszczania, które są naprawione:

Przykłady bez przeciągania i upuszczania, które są interaktywne:

Przykłady z przeciąganiem i upuszczaniem:

Ta odpowiedź jest oparta off Glenn Dayton za odpowiedź .

balupton
źródło
1

js-graph.it obsługuje ten przypadek użycia, jak widać w jego przewodniku dla początkujących , obsługując przeciąganie elementów bez nakładania się połączeń. Wygląda na to, że nie obsługuje edycji / tworzenia połączeń. Nie wydaje się, że jest już utrzymywany.

balupton
źródło