flèches Dessin sur une page HTML pour visualiser les liens sémantiques entre travées textuelles

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

  •  23-08-2019
  •  | 
  •  

Question

J'ai une page HTML avec une textuelles travées balisées quelque chose comme ceci:

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

i.e.. chaque travée a une ID et fait référence à zéro ou plusieurs travées par l'intermédiaire de leurs identificateurs.

Je voudrais visualiser ces références sous forme de flèches.

Deux questions:

  • Comment puis-je mapper un ID d'une travée aux coordonnées d'écran du rendu de la durée?
  • Comment puis-je dessiner des flèches allant d'un rendu à un autre?

La solution doit travailler dans Firefox, travaillant dans d'autres navigateurs est un plus, mais pas vraiment nécessaire. La solution pourrait utiliser jQuery, ou une autre bibliothèque JavaScript léger.

Était-ce utile?

La solution

Vous avez un couple d'options: svg ou toile .

De l'apparence de celui-ci vous n'avez pas besoin de ces flèches pour avoir une forme mathématique particulière, il vous suffit de les passer entre les éléments.

WireIt . Jetez un oeil à cette WireIt Démo ( qui a été désapprouvée ). Il utilise une balise de canvas pour chaque fil entre les divs de dialogue flottantes, tailles et positions alors chaque élément de canvas pour donner l'apparence d'une ligne reliant juste au bon endroit. Vous pourriez avoir à mettre en œuvre une pointe de flèche variable supplémentaire, à moins que vous ne me dérange pas les flèches à venir pour chaque élément au même angle.

Modifier : la démo a été désapprouvée .

Modifier : Ignorer cette réponse, @Phil H a clouée

Autres conseils

Cette capturé mon intérêt assez longtemps pour produire un petit test. Le code est ci-dessous, et vous pouvez voir en action

capture d

Il répertorie toutes les portées de la page (pourraient vouloir limiter ce à seulement ceux avec ids commençant par T si cela est approprié), et utilise l'attribut 'ids de construire la liste des liens. L'utilisation d'un élément de toile derrière les travées, il dessine des flèches arc alternativement au-dessus et en dessous des travées pour chaque travée source.

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

Et vous avez juste besoin d'un appel quelque part à la fonction draw ():

<body onload="draw();"> 

Ensuite, une toile derrière l'ensemble des travées.

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

Les futures modifications, pour autant que je peux voir:

  • Aplatir haut de flèches plus longues
  • refactoring pour pouvoir dessiner des flèches non horizontales: ajouter une nouvelle toile pour chaque
  • Utilisez une meilleure routine pour obtenir les compensations totales des éléments de toile et de l'échelle.

[Modifier décembre 2011: fixe, merci @Palo]

L'espoir qui est aussi utile que c'était amusant.

Une grande bibliothèque pour les flèches est JointJS qui est basée sur Raphael comme indiqué ci-dessus. Avec JointJS, vous pouvez facilement dessiner des flèches avec des courbes ou des sommets sans choses compliquées; -)

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

Ceci définit une flèche « J34 » qui relie deux points js s3 s4 avec. Tout le reste peut être lu dans la documentation de JointJS.

Vous pouvez essayer cette bibliothèque - c'est un truc intelligent, espérons que ça aide.

EDIT: Comme ce lien est mort, voici un autre lien Archive.org .

J'essaie d'aller avec les technologies Web ouvertes chaque fois que possible, mais la vérité est que HTML et JavaScript (ou jQuery) ne sont pas les outils pour ce travail particulier (triste mais vrai), d'autant plus que les diagrammes que vous dessinez augmentation complexité.

Par contre, Flash a été fait pour cela. Beaucoup moins du code ActionScript 3.0 serait nécessaire pour analyser que XML, mise en page votre texte (avec plus de contrôle sur les polices et super / indices) et rendre les courbes (voir flash.display.Graphics classe méthodes comme curveTo). Dans l'ensemble, vous regarderez moins de code, mieux maintenabilité, moins hacks, une plus grande compatibilité et les bibliothèques de dessin plus stables.

Bonne chance avec le projet.

Si vous n'avez pas besoin flèches courbes, vous pouvez utiliser absolument placé divs dessus ou en dessous de la liste. Vous pouvez ensuite utiliser le style CSS pour les divs ainsi que quelques images qui composent la tête de flèche. Ci-dessous un exemple en utilisant l'icône mis du projet jQuery UI (désolé long URL).

Voici le CSS pour obtenir les choses ont commencé:

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

Maintenant, nous pouvons commencer à assembler divs flèche. Par exemple, pour le style de la flèche de « exige » à « promoteur » dans votre exemple ci-dessus, vous pourriez faire à gauche, en bas, bordures droite et sur la div avec et vers le haut de la flèche graphique en haut à gauche de la div.

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

Les styles en ligne seraient doivent être appliquées par le script après que vous avez compris l'emplacement des choses que vous auriez besoin de se connecter. Disons que votre liste ressemble à ceci:

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

Ensuite, le script suivant la position de votre flèche:

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

Allez-y coller les exemples ci-dessus dans une page html vierge. Il est un peu soignée.

Comme d'autres l'ont mentionné, Javascript et HTML ne sont pas de bons outils pour ce genre de chose.

John Resig a écrit une implémentation de Processing.org en JavaScript . Il utilise l'élément de toile, de sorte qu'il fonctionne dans les versions modernes de Firefox, mais il ne fonctionnera pas dans tous les navigateurs. Si vous ne vous préoccupez Firefox, ce serait sans doute la voie à suivre.

Vous pourriez être en mesure d'utiliser SVG, mais encore une fois, ce n'est pas pris en charge dans tous les navigateurs.

Je avais besoin d'une solution similaire, et je cherchais dans RaphaelJS JavaScript Library . Par exemple, vous pouvez dessiner une flèche droite de (x1,y1) à (x2,y2) avec:

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

Je n'ai pas comprendre comment dessiner une flèche incurvée, mais je suis sûr qu'il est possible.

Vous pouvez obtenir la flèche courbe se termine à l'aide d'une poignée de divs position:absolute avec background-image réglé sur transparent ... GIFs un ensemble pour le début (en haut et en bas) ... un div bacground:repeat pour milieu expandible, et une autre paire pour les extrémités (haut et bas).

Vous pouvez utiliser cette bibliothèque : il suffit d'annoter vos lignes SVG avec les ids de l'élément source et cible . Il utilise MutationObserver pour observer les changements dans les éléments connectés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top