Dibujar una línea de conexión entre dos elementos [cerrado]


105

¿Cómo puedo trazar una línea entre dos o más elementos para conectarlos? Cualquier combinación de HTML / CSS / JavaScript / SVG / Canvas está bien.

Si su respuesta es compatible con alguno de estos, menciónelo:

  • elementos arrastrables
  • conexiones que se pueden arrastrar / editar
  • evitación de superposición de elementos

Esta pregunta se ha actualizado para consolidar las numerosas variaciones de la misma.

Respuestas:


164

jsPlumb es una opción disponible que admite arrastrar y soltar, como se ve en sus numerosas demostraciones , incluida la demostración del diagrama de flujo .

Está disponible en una edición comunitaria gratuita y una edición de kit de herramientas de pago.

La edición Toolkit envuelve la edición Community con una capa de enlace de datos integral, así como varios widgets de interfaz de usuario para crear aplicaciones e integraciones para bibliotecas populares, y tiene licencia comercial.


4
Kit de herramientas increíble, pero ten cuidado: ¡no es gratis! Quieren que compre una licencia si tiene la intención de alojar públicamente o vender dentro de sus propios productos (consulte jsplumbtoolkit.com/purchase ).
Chris

50

Unir líneas con svgs valió la pena para mí, y funcionó perfectamente ... En primer lugar, Scalable Vector Graphics (SVG) es un formato de imagen vectorial basado en XML para gráficos bidimensionales con soporte para interactividad y animación. Las imágenes SVG y sus comportamientos se definen en archivos de texto XML. puede crear un svg en HTML usando una <svg>etiqueta. Adobe Illustrator es uno de los mejores programas utilizados para crear svgs complejos utilizando rutas.

Procedimiento para unir dos divs usando una línea:

  1. crea dos divs y dales la posición que necesites

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

    (en aras de la explicación, estoy haciendo un estilo en línea, pero siempre es bueno crear un archivo css separado para el estilo)

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

    La etiqueta de línea nos permite dibujar una línea entre dos puntos especificados (x1, y1) y (x2, y2). (para una referencia, visite w3schools). Aún no los hemos especificado. porque usaremos jQuery para editar los atributos (x1, y1, x2, y2) de la etiqueta de línea.

  3. en <script>escritura de etiqueta

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

    Usé selectores para seleccionar los dos divs y la línea ...

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

    El position()método jQuery nos permite obtener la posición actual de un elemento. Para obtener más información, visite https://api.jquery.com/position/ (también puede usar el offset()método)

Ahora que hemos obtenido todas las posiciones que necesitamos, podemos trazar la línea de la siguiente manera ...

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

El .attr()método jQuery se utiliza para cambiar los atributos del elemento seleccionado.

Todo lo que hicimos en la línea anterior es cambiar los atributos de la línea de

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

a

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

como position()devuelve dos valores, uno 'left' y otro 'top', podemos acceder fácilmente a ellos usando .top y .left usando los objetos (aquí pos1 y pos2) ...

Ahora la etiqueta de línea tiene dos coordenadas distintas para trazar una línea entre dos puntos.

Sugerencia: agregue detectores de eventos según necesite para divs

Consejo: asegúrese de importar la biblioteca jQuery primero antes de escribir cualquier cosa en la etiqueta del script

Después de agregar coordenadas a través de JQuery ... se verá algo así

El siguiente fragmento es solo para fines de demostración, siga los pasos anteriores para obtener la solución correcta

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


3
No copie y pegue la misma respuesta a varias preguntas. En su lugar, personalice las respuestas a las preguntas individuales.
Andy

2
Necesito poner el svg en ancho y alto al 100% en segundo plano usando z-index -1 pero funciona como un encanto.
steven


31
Votantes negativos, por favor comenten la razón para hacerlo ... He publicado las mismas respuestas para ambas preguntas porque estoy seguro de que esta respuesta es aplicable a ambas preguntas ... Si dos preguntas son relevantes, entonces sus respuestas también pueden ser relevantes ... . No hice nada incorrecto ...
Ani

6

También tuve el mismo requisito hace unos días.

Usé un svg de ancho y alto completo y lo agregué debajo de todos mis divs y agregué líneas a estos svg dinámicamente.

Mira cómo lo hice aquí 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);
});

No parece funcionar en Safari versión 12.0.1 (14606.2.104.1.1)
balupton



2

Recientemente, intenté desarrollar una aplicación web simple que usa componentes de arrastrar y soltar y tiene líneas que los conectan. Me encontré con estas dos bibliotecas de JavaScript simples y sorprendentes:

  1. Plain Draggable : biblioteca simple y de alto rendimiento que permite arrastrar elementos HTML / SVG.
  2. Línea guía: dibuje una línea guía en su página web


1



1

js-graph.it admite este caso de uso, como se ve en su guía de introducción , que admite elementos de arrastre sin superposiciones de conexión. No parece que sea compatible con la edición / creación de conexiones. Parece que ya no se mantiene.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.