Zeichnung Pfeile auf einer HTML-Seite, um semantische Verknüpfungen zwischen Text Spannweiten zu visualisieren

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

  •  23-08-2019
  •  | 
  •  

Frage

Ich habe eine HTML-Seite mit einigem Text etwas wie dies markiert Spannen:

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

d. Jeder Bereich hat eine ID und nimmt Bezug auf null oder mehr Spannweiten über ihre IDs.

Ich mag diese Referenzen als Pfeile sichtbar zu machen.

Zwei Fragen:

  • Wie kann ich eine ID einer Spanne auf die Bildschirmkoordinaten der Wiedergabe der Spanne Karte?
  • Wie zeichne ich Pfeile von einem Rendering zu einem anderen gehen?

Die Lösung sollte in Firefox arbeiten, in anderen Browsern ist ein Plus, aber nicht wirklich notwendig. Die Lösung könnte jQuery oder eine andere leichte JavaScript-Bibliothek verwenden.

War es hilfreich?

Lösung

Sie haben ein paar Optionen: svg oder Leinwand .

Von den Blicken von ihm Sie diese Pfeile brauchen keine besondere mathematische Form zu haben, müssen Sie sie nur zwischen den Elementen gehen.

Versuchen Sie WireIt . Werfen Sie einen Blick auf diese WireIt Demo (, die veraltet ist ). Es verwendet ein canvas Tag für jeden einzelnen Draht zwischen den schwimmenden Dialog divs, dann Größen und Positionen jedes canvas Element an genau der richtigen Stelle das Aussehen einer Verbindungsleitung zu geben. Sie können einen weiteren rotierenden Pfeilspitzen zu implementieren haben, es sei denn, Sie zu jedem Element im gleichen Winkel kommt in nicht auf die Pfeile dagegen.

Bearbeiten : die Demo veraltet.

Bearbeiten : Ignorieren Sie diese Antwort, @Phil H genagelt es

Andere Tipps

Dies erfaßt mein Interesse für lange genug, um einen kleinen Test zu produzieren. Der Code ist unten, und Sie können

Screenshot

Es listet alle Spannweiten auf der Seite (vielleicht, dass nur diejenigen mit ids beginnend mit T einschränken möchten, ob das geeignet ist) und verwendet das Attribut ‚Ids‘ die Liste der Links zu bauen. Verwendung eines Leinenelement hinter den Spannweiten, zieht es arc Pfeile abwechselnd über und unter den Spannweiten für jede Quelle Spanne.

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

Und Sie brauchen nur einen Anruf irgendwo auf die draw () Funktion:

<body onload="draw();"> 

Dann wird eine Leinwand hinter dem Satz von Spannweiten.

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

Zukünftige Änderungen, soweit ich sehen kann:

  • Abflachen der Spitze von mehr Pfeile
  • Refactoring der Lage sein, nicht horizontale Pfeile zu ziehen: eine neue Leinwand für jedes Add
  • eine bessere Routine Verwenden Sie die Gesamt Offsets der Leinwand und Spanne Elemente zu erhalten.

[Bearbeiten Dezember 2011: Fest dank @Palo]

Hope, die als nützlich ist, wie es war Spaß.

Eine große Bibliothek für Pfeile ist JointJS , die auf Raphael basiert wie oben gezeigt. Mit JointJS können Sie leicht Pfeile mit Kurven oder Ecken ohne komplizierte Sachen ziehen; -)

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

Dies definiert einen Pfeil ‚J34‘, die zwei js Artikel s3 mit s4 verbindet. Alles andere kann in der Dokumentation von JointJS gelesen werden.

Sie könnten versuchen, diese Bibliothek - es ist sehr klug Sachen, hoffe, es hilft.

EDIT: Da dieser Link ist tot, hier ist eine andere Verbindung von Archive.org .

Ich versuche, mit offenen Web-Technologien zu gehen, wo immer möglich, aber die Wahrheit ist, dass HTML & JavaScript (oder jQuery) sind nicht die Werkzeuge für diesen speziellen Job (traurig, aber wahr), zumal die Diagramme Sie Anstieg sind Zeichnung in Komplexität.

Auf der anderen Seite, Flash- wurde dafür gemacht. Deutlich weniger Actionscript 3.0-Code erforderlich sein würde, dass die XML-Analyse, das Layout Ihren Text (mit mehr Kontrolle über Schriftarten und Super- / subscripts) und die Kurven machen (siehe flash.display.Graphics Klasse Methoden wie curveTo). Insgesamt werden Sie weniger Code suchen werden, besser Wartbarkeit, weniger Hacks, eine größere Kompatibilität und stabile Zeichnung Bibliotheken.

Viel Glück mit dem Projekt.

Wenn Sie nicht gebogene Pfeile müssen, können Sie absolut divs oben oder unten in der Liste positioniert verwenden. Sie könnten dann CSS verwenden, um diese divs plus ein paar Bilder, um Stil, der die Pfeilspitze bilden. Unten ist ein Beispiel des Symbol aus dem jQuery UI-Projekt (sorry über die lange URL).

gesetzt mit

Hier ist die CSS Dinge zu erhalten begonnen:

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

Jetzt können wir beginnen Pfeil divs zu montieren. Zum Beispiel auf den Pfeil, um Stil von oben zu „Promotor“ in Ihrem Beispiel „erfordert“, könnten Sie verlassen haben, untere und rechter Rand auf der div mit und nach oben weisender Pfeil Grafik in der linken oberen Ecke des div gegenüber.

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

Die Inline-Stile werden würde müssen durch Skript angewendet werden, nachdem Sie die Lage der Dinge herausgefunden Sie müssten verbinden. Lassen Sie uns sagen, dass Ihre Liste wie folgt aussieht:

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

Dann wird das folgende Skript positionieren Sie den Pfeil:

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

Gehen Sie voran und die Beispiele oben in eine leere HTML-Seite einfügen. Es ist eine Art ordentlich.

Wie andere erwähnt haben, Javascript und HTML sind nicht gute Werkzeuge für diese Art der Sache.

John Resig schrieb eine Implementierung von Processing.org in JavaScript . Es verwendet das Canvas-Element, so dass es in modernen Versionen von Firefox funktionieren, aber es wird nicht alle Browser arbeiten. Wenn Sie nur über Firefox kümmern, wäre dies wohl der Weg zu gehen.

Sie könnten in der Lage sein, SVG zu verwenden, aber auch dies ist nicht in allen Browsern unterstützt.

Ich brauchte eine ähnliche Lösung, und ich war auf der Suche in RaphaelJS JavaScript-Bibliothek . Zum Beispiel können Sie einen geraden Pfeil von (x1,y1) ziehen (x2,y2) mit:

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];
}

Ich habe nicht herausfinden, wie ein gebogenen Pfeil zu zeichnen, aber ich bin sicher, dass es möglich ist.

Sie könnten den gekrümmte Pfeil endet mit einer Handvoll position:absolute divs mit background-image auf transparente GIFs bekommen ... ein Set für Anfang (oben und unten) ... ein bacground:repeat div für erweiterbare Mitte und ein Paar für die Ende (oben und unten).

Sie können mit diese Bibliothek : nur Ihre SVG-Linien mit den IDs der Quelle & Zielelement mit Anmerkungen versehen . Es verwendet MutationObserver Änderungen in den verbundenen Elementen zu beobachten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top