Disegnare frecce su una pagina HTML per visualizzare collegamenti semantici tra span testuali

StackOverflow https://stackoverflow.com/questions/554167

  •  23-08-2019
  •  | 
  •  

Domanda

Ho una pagina HTML con alcuni intervalli testuali contrassegnati in questo modo:

...
<span id="T2" class="Protein">p50</span>
...
<span id="T3" class="Protein">p65</span>
...
<span id="T34" ids="T2 T3" class="Positive_regulation">recruitment</span>
...

Cioè.ogni span ha un ID e fa riferimento a zero o più span tramite i relativi ID.

Vorrei visualizzare questi riferimenti come frecce.

Due domande:

  • Come posso mappare un ID di una campata alle coordinate dello schermo del rendering della campata?
  • Come faccio a disegnare le frecce che passano da un rendering all'altro?

La soluzione dovrebbe funzionare in Firefox, lavorare in altri browser è un vantaggio ma non è realmente necessario.La soluzione potrebbe utilizzare jQuery o qualche altra libreria JavaScript leggera.

È stato utile?

Soluzione

Hai un paio di opzioni: svg o tela .

Dagli sguardi di esso non ti servono queste frecce per avere una particolare forma matematica, basta loro di andare tra gli elementi.

WireIt . Date un'occhiata a questo WireIt Demo ( che è stato deprecato ). Esso utilizza un tag canvas per ogni singolo filo tra i divs dialogo galleggianti, quindi dimensioni e posizioni ciascun elemento canvas per dare l'aspetto di una linea di collegamento proprio al punto giusto. Potrebbe essere necessario implementare una punta di freccia rotante aggiuntivo, a meno che non si mente le frecce entrano ad ogni elemento con la stessa angolazione.

Modifica : la demo è stato deprecato .

Modifica : Ignorare questa risposta, @Phil H è inchiodato

testuali-campate -links-tra-

Altri suggerimenti

Questa catturato il mio interesse per un tempo sufficiente a produrre un piccolo test. Il codice è al di sotto, e si può vederlo in azione

screenshot

E 'elenca tutte le campate nella pagina (potrebbe voler limitare tale solo a quelli con gli ID che iniziano con T se questo è idoneo), e usa l'attributo 'id' per costruire la lista di link. Utilizzando un elemento di tela dietro le campate, trae frecce arco alternativamente sopra e sotto le campate per ogni campata sorgente.

<script type="application/x-javascript"> 

function generateNodeSet() {
  var spans = document.getElementsByTagName("span");
  var retarr = [];
  for(var i=0;i<spans.length; i++) { 
     retarr[retarr.length] = spans[i].id; 
  } 
  return retarr; 
} 

function generateLinks(nodeIds) { 
  var retarr = []; 
  for(var i=0; i<nodeIds.length; i++) { 
    var id = nodeIds[i];
    var span = document.getElementById(id); 
    var atts = span.attributes; 
    var ids_str = false; 
    if((atts.getNamedItem) && (atts.getNamedItem('ids'))) { 
      ids_str = atts.getNamedItem('ids').value; 
    } 
    if(ids_str) { 
      retarr[id] = ids_str.split(" ");
    }
  } 
  return retarr; 
} 

// degrees to radians, because most people think in degrees
function degToRad(angle_degrees) {
   return angle_degrees/180*Math.PI;
}
// draw a horizontal arc
//   ctx: canvas context;
//   inax: first x point
//   inbx: second x point
//   y: y value of start and end
//   alpha_degrees: (tangential) angle of start and end
//   upside: true for arc above y, false for arc below y.
function drawHorizArc(ctx, inax, inbx, y, alpha_degrees, upside)
{
  var alpha = degToRad(alpha_degrees);
  var startangle = (upside ? ((3.0/2.0)*Math.PI + alpha) : ((1.0/2.0)*Math.PI - alpha));
  var endangle = (upside ? ((3.0/2.0)*Math.PI - alpha) : ((1.0/2.0)*Math.PI + alpha));

  var ax=Math.min(inax,inbx);
  var bx=Math.max(inax,inbx);

  // tan(alpha) = o/a = ((bx-ax)/2) / o
  // o = ((bx-ax)/2/tan(alpha))
  // centre of circle is (bx+ax)/2, y-o
  var circleyoffset = ((bx-ax)/2)/Math.tan(alpha);
  var circlex = (ax+bx)/2.0;
  var circley = y + (upside ? 1 : -1) * circleyoffset;
  var radius = Math.sqrt(Math.pow(circlex-ax,2) + Math.pow(circley-y,2));

  ctx.beginPath();
  if(upside) {
      ctx.moveTo(bx,y);
    ctx.arc(circlex,circley,radius,startangle,endangle,1);
  } else {
    ctx.moveTo(bx,y);
    ctx.arc(circlex,circley,radius,startangle,endangle,0);
  }
  ctx.stroke();
}


// draw the head of an arrow (not the main line)
//  ctx: canvas context
//  x,y: coords of arrow point
//  angle_from_north_clockwise: angle of the line of the arrow from horizontal
//  upside: true=above the horizontal, false=below
//  barb_angle: angle between barb and line of the arrow
//  filled: fill the triangle? (true or false)
function drawArrowHead(ctx, x, y, angle_from_horizontal_degrees, upside, //mandatory
                       barb_length, barb_angle_degrees, filled) {        //optional
   (barb_length==undefined) && (barb_length=13);
   (barb_angle_degrees==undefined) && (barb_angle_degrees = 20);
   (filled==undefined) && (filled=true);
   var alpha_degrees = (upside ? -1 : 1) * angle_from_horizontal_degrees; 

   //first point is end of one barb
   var plus = degToRad(alpha_degrees - barb_angle_degrees);
   a = x + (barb_length * Math.cos(plus));
   b = y + (barb_length * Math.sin(plus));

   //final point is end of the second barb
   var minus = degToRad(alpha_degrees + barb_angle_degrees);
   c = x + (barb_length * Math.cos(minus));
   d = y + (barb_length * Math.sin(minus));

   ctx.beginPath();
   ctx.moveTo(a,b);
   ctx.lineTo(x,y);
   ctx.lineTo(c,d);
   if(filled) {
    ctx.fill();
   } else {
    ctx.stroke();
   }
   return true;
}

// draw a horizontal arcing arrow
//  ctx: canvas context
//  inax: start x value
//  inbx: end x value
//  y: y value
//  alpha_degrees: angle of ends to horizontal (30=shallow, >90=silly)
function drawHorizArcArrow(ctx, inax, inbx, y,                 //mandatory
                           alpha_degrees, upside, barb_length) { //optional
   (alpha_degrees==undefined) && (alpha_degrees=45);
   (upside==undefined) && (upside=true);
   drawHorizArc(ctx, inax, inbx, y, alpha_degrees, upside);
   if(inax>inbx) { 
    drawArrowHead(ctx, inbx, y, alpha_degrees*0.9, upside, barb_length); 
   } else { 
    drawArrowHead(ctx, inbx, y, (180-alpha_degrees*0.9), upside, barb_length); 
   }
   return true;
}


function drawArrow(ctx,fromelem,toelem,    //mandatory
                     above, angle) {        //optional
  (above==undefined) && (above = true);
  (angle==undefined) && (angle = 45); //degrees 
  midfrom = fromelem.offsetLeft + (fromelem.offsetWidth / 2) - left - tofromseparation/2; 
  midto   =   toelem.offsetLeft + (  toelem.offsetWidth / 2) - left + tofromseparation/2;
  //var y = above ? (fromelem.offsetTop - top) : (fromelem.offsetTop + fromelem.offsetHeight - top);
  var y = fromelem.offsetTop + (above ? 0 : fromelem.offsetHeight) - canvasTop;
  drawHorizArcArrow(ctx, midfrom, midto, y, angle, above);
}

    var canvasTop = 0;
function draw() { 
  var canvasdiv = document.getElementById("canvas");
  var spanboxdiv = document.getElementById("spanbox");
  var ctx = canvasdiv.getContext("2d");

  nodeset = generateNodeSet(); 
  linkset = generateLinks(nodeset);
  tofromseparation = 20;

  left = canvasdiv.offsetLeft - spanboxdiv.offsetLeft;
  canvasTop = canvasdiv.offsetTop - spanboxdiv.offsetTop; 
  for(var key in linkset) {  
    for (var i=0; i<linkset[key].length; i++) {  
      fromid = key; 
      toid = linkset[key][i]; 
      var above = (i%2==1);
      drawArrow(ctx,document.getElementById(fromid),document.getElementById(toid),above);
    } 
  } 
} 

</script> 

E basta una chiamata da qualche parte per la funzione draw ():

<body onload="draw();"> 

Poi una tela dietro la serie di campate.

<canvas style='border:1px solid red' id="canvas" width="800" height="7em"></canvas><br /> 
<div id="spanbox" style='float:left; position:absolute; top:75px; left:50px'>
<span id="T2">p50</span>
...
<span id="T3">p65</span> 
...
<span id="T34" ids="T2 T3">recruitment</span>
</div> 

modifiche future, per quanto posso vedere:

  • appiattimento all'inizio della frecce più lunghe
  • refactoring per essere in grado di disegnare frecce non orizzontali: aggiungere una nuova tela per ogni
  • Utilizzare una routine migliore per ottenere gli offset totale degli elementi di tela e span.

[Modifica dicembre 2011: fisso, grazie @Palo]

La speranza che è tanto utile quanto è stato divertente.

Una grande libreria per frecce è JointJS che si basa su Raphael come mostrato sopra. Con JointJS si può facilmente disegnare frecce con curve o vertici senza alcun roba complicata; -)

var j34 = s3.joint(s4, uml.arrow).setVertices(["170 130", "250 120"]);

Questo definisce una freccia 'J34' che collega due js Articoli s3 con s4. Tutto il resto si può leggere nella documentazione di JointJS.

Si potrebbe provare a questa libreria - è roba molto intelligente, spero che aiuta.

EDIT: Come questo link è morto, ecco un altro link da Archive.org .

cerco di andare con le tecnologie web aperte per quanto possibile, ma la verità è che l'HTML e JavaScript (o jQuery) non sono gli strumenti di questo particolare lavoro (triste ma vero), tanto più che gli schemi si sta disegnando in aumento complessità.

D'altra parte, Flash è stato fatto per questo. Significativamente meno codice ActionScript 3.0 sarebbe necessario per analizzare che XML, il layout il testo (con un maggiore controllo sui tipi di carattere e super / indici) e rendere le curve (si veda il classe flash.display.Graphics metodi come curveTo). Nel complesso si troverà di fronte al meno codice, una migliore manutenibilità, un minor numero di hack, maggiore compatibilità e le librerie di disegno più stabili.

Buona fortuna con il progetto.

Se non avete bisogno di frecce curve, è possibile utilizzare posizionato in modo assoluto div sopra o sotto la lista. È quindi possibile utilizzare i CSS per lo stile quei div più un paio di immagini che compongono la punta della freccia. Di seguito è riportato un esempio utilizzando il set di icone dal progetto jQuery UI (dispiace per il lungo URL).

Ecco il CSS per ottenere le cose iniziato:

<style>
 .below{
     border-bottom:1px solid #000;
     border-left:1px solid #000;
     border-right:1px solid #000;
 }
 .below span{
    background-position:0px -16px;
    top:-8px;
 }
 .above{
     border-top:1px solid #000;
     border-left:1px solid #000;
     border-right:1px solid #000;
 }
 .above span{
    background-position:-64px -16px;
    bottom:-8px;
 }

 .arrow{
    position:absolute;
    display:block;
    background-image:url(http://jquery-ui.googlecode.com/svn/trunk/themes/base/images/ui-icons_454545_256x240.png);
    width:16px;
    height:16px;
    margin:0;
    padding:0;
 }

.left{left:-8px;}

.right{right:-9px;}

</style>

Ora possiamo iniziare ad assemblare div freccia. Per esempio, per lo stile della freccia da "richiede" a "promoter" nel tuo esempio di cui sopra, si potrebbe fare a sinistra, in basso, e confina a destra sul div con e rivolto verso l'alto freccia grafico in alto a sinistra del div.

<div class='below' style="position:absolute;top:30px;left:30px;width:100px;height:16px">
   <span class='arrow left'></span>
</div>
sarebbe bisogno

Gli stili inline da applicarsi da parte di script dopo aver capito le posizioni delle cose che avrebbero bisogno di connettersi. Diciamo che la vostra lista simile a questa:

<span id="promoter">Promoter</span><span>Something Else</span><span id="requires">Requires</span>

Poi il seguente script posizionerà la freccia:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script> 
<script>
$(function(){
 var promoterPos=$("#promoter").offset();
 var requiresPos=$("#requires").offset();
 $("<div class='below'><span class='arrow left'></span></div>")
 .css({position:"absolute",left:promoterPos.left,right:promoterPos.top+$("#promoter").height()})
 .width(requiresPos.left-promoterPos.left)
 .height(16)
 .appendTo("body");
});
</script>

Vai avanti e incollare gli esempi di cui sopra in una pagina html vuota. È una specie di pulito.

Come altri hanno detto, JavaScript e HTML non sono buoni strumenti per questo genere di cose.

John Resig scritto un'implementazione di Processing.org in JavaScript . Esso utilizza l'elemento canvas, in modo da funzionare in versioni moderne di Firefox, ma non funziona in tutti i browser. Se vi interessa soltanto Firefox, questo sarebbe probabilmente la strada da percorrere.

Potreste essere in grado di utilizzare SVG, ma ancora una volta, questo non è supportato in tutti i browser.

ho bisogno di una soluzione simile, e stavo guardando in RaphaelJS JavaScript Library . Per esempio è possibile disegnare una freccia dritta da (x1,y1) a (x2,y2) con:

Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
  var angle = Math.atan2(x1-x2,y2-y1);
  angle = (angle / (2 * Math.PI)) * 360;
  var arrowPath = this.path(“M” + x2 + ” ” + y2 + ” L” + (x2 - size) + ” ” + (y2 - size) + ” L” + (x2 - size) + ” ” + (y2 + size) + ” L” + x2 + ” ” + y2 ).attr(“fill”,”black”).rotate((90+angle),x2,y2);
  var linePath = this.path(“M” + x1 + ” ” + y1 + ” L” + x2 + ” ” + y2);
  return [linePath,arrowPath];
}

Non ho capire come disegnare una freccia curva, ma sono sicuro che sia possibile.

Si potrebbe ottenere la freccia curva termina con una manciata di div position:absolute con background-image impostato GIF trasparenti ... un insieme di inizio (superiore e inferiore) ... un div bacground:repeat per mezzo espandibile, e un'altra coppia per le estremità (superiore e inferiore).

Puoi usare questa biblioteca:annota semplicemente le tue righe SVG con gli ID dell'elemento source e target.Utilizza MutationObserver osservare i cambiamenti negli elementi collegati.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top