Dibujo flechas en una página HTML para visualizar los enlaces semánticos entre vanos de texto

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

  •  23-08-2019
  •  | 
  •  

Pregunta

Tengo una página HTML con cierto Pruebas palmos marcado algo como esto:

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

es decir. cada tramo tiene un ID y una referencia a cero o más tramos a través de sus IDs.

Me gustaría visualizar estas referencias como flechas.

Dos preguntas:

  • ¿Cómo puedo asignar un ID de que un lapso de las coordenadas de la pantalla de la prestación del vano?
  • ¿Cómo puedo dibujar flechas que van de uno a otro de representación?

La solución debe trabajar en Firefox, trabajando en otros navegadores es una ventaja, pero no es realmente necesario. La solución podría usar jQuery, o alguna otra librería de JavaScript ligero.

¿Fue útil?

Solución

Usted tiene un par de opciones: SVG o lienzo .

Desde el aspecto de que usted no necesita estas flechas para tener cualquier forma matemática en particular, sólo los necesita para ir entre los elementos.

Trate WireIt . Echar un vistazo a este WireIt demostración ( que ha quedado en desuso ). Se utiliza una etiqueta canvas para cada cable individual entre los divs diálogo flotante, a continuación, tamaños y posiciones cada elemento canvas para dar la apariencia de una línea de conexión en el lugar correcto. Puede que tenga que poner en práctica una punta de flecha giratoria adicional, a menos que no le importe las flechas que llegan a cada elemento en el mismo ángulo.

Editar la demo ya no se utiliza .

Editar : Ignorar esta respuesta, @Phil H clavó

Otros consejos

Esta capturó mi interés durante el tiempo suficiente para producir una pequeña prueba. El código está por debajo, y se puede verlo en acción

pantalla

En él se enumeran todos los tramos de la página (puede ser que desee para restringir que sólo aquellos con los identificadores que comienzan con T si es adecuado), y utiliza el atributo 'id de' construir la lista de enlaces. El uso de un elemento de tela detrás de las luces, dibuja flechas de arco alternativamente por encima y por debajo de los tramos para cada tramo de origen.

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

Y sólo tiene una llamada en algún lugar a la función draw ():

<body onload="draw();"> 

A continuación, un lienzo detrás del conjunto de vanos.

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

modificaciones futuras, por lo que se puede ver:

  • aplanamiento de la parte superior de las flechas más largas
  • Refactoring para poder dibujar flechas no horizontales: añadir un nuevo lienzo para cada
  • Utilice una mejor rutina para obtener los desplazamientos totales de los elementos de la lona y span.

[Editar diciembre 2011: fijo, gracias @Palo]

La esperanza que es tan útil, ya que fue muy divertido.

A gran biblioteca de flechas es JointJS que se basa en Raphael como se muestra arriba. Con JointJS se puede dibujar fácilmente flechas con curvas o vértices sin ningún tipo de cosas complicadas; -)

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

Esto define una flecha 'J34' que conecta dos js artículos s3 con s4. Todo lo demás se puede leer en la documentación de JointJS.

Usted podría intentar esta biblioteca - son cosas muy inteligente, espero que ayude.

EDIT: Como este enlace está muerto, aquí es otro eslabón de la Archive.org .

Trato de ir con tecnologías web abiertas siempre que sea posible, pero la verdad es que el HTML y JavaScript (o jQuery) no son las herramientas para este trabajo en particular (triste pero cierto), sobre todo porque los diagramas que está dibujando aumento de complejidad.

Por otro lado, Flash fue hecho para esto. Es significativo que se requeriría menos código ActionScript 3.0 para analizar el XML, la disposición de su texto (con un mayor control sobre las fuentes y Super / subíndices) y hacer que las curvas (véase la clase flash.display.Graphics métodos como curveTo). En general se le busca por lo menos código, una mejor capacidad de mantenimiento, menor número de cortes, mayor compatibilidad y bibliotecas de dibujo más estables.

Buena suerte con el proyecto.

Si usted no necesita flechas curvas, se puede usar con posición absoluta divs encima o debajo de la lista. A continuación, puede utilizar CSS para el estilo de los divs además de un par de imágenes que componen la punta de la flecha. A continuación se muestra un ejemplo usando el conjunto de iconos del proyecto jQuery UI (lo de la URL larga).

Aquí está la CSS tome la iniciativa:

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

Ahora podemos empezar a montar divs de flecha. Por ejemplo, el estilo de la flecha desde "requiere" a "promotor" en su ejemplo anterior, se podría hacer izquierda, abajo, derecha y las fronteras en el div con y hacia arriba flecha gráfico en la parte superior izquierda de la div.

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

Los estilos en línea para ser aplicado por el guión después de descubierto la ubicación de las cosas que se necesitan para conectar. Digamos que su lista es el siguiente:

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

A continuación, el siguiente script posicionar su flecha:

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

Vaya por delante y pegar los ejemplos anteriores en una página HTML en blanco. Es una especie de limpia.

Como otros han mencionado, JavaScript y HTML no son buenas herramientas para este tipo de cosas.

una implementación de Processing.org en JavaScript . Se utiliza el elemento canvas, por lo que funciona en las versiones modernas de Firefox, pero no va a trabajar en todos los navegadores. Si sólo se preocupan por Firefox, esto sería probablemente el camino a seguir.

Usted puede ser capaz de utilizar SVG, pero de nuevo, esto no es compatible con todos los navegadores.

que necesitaba una solución similar, y yo estaba buscando en RaphaelJS JavaScript Library . Por ejemplo, puede dibujar una flecha recta desde (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];
}

No he averiguar cómo dibujar una flecha curva, pero estoy seguro de que es posible.

Se puede obtener la flecha curva termina usando un puñado de divs position:absolute con background-image ajustado a GIF transparentes ... un conjunto de principios (superior e inferior) ... un div bacground:repeat por medio expandible, y otro par de los extremos (superior e inferior).

Puede utilizar esta biblioteca : acaba de anotar sus líneas SVG con los identificadores de elemento de origen y de destino . Utiliza MutationObserver para observar los cambios en los elementos conectados.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top