2011-11-21 17 views
8

Sto lavorando all'applicazione di mappe concettuali, che ha una serie di nodi e collegamenti. Ho collegato i collegamenti ai nodi usando il centro del nodo come riferimento. Poiché ho nodi con dimensioni e forme diverse, non è consigliabile disegnare la freccia per il collegamento specificando l'altezza o la larghezza della forma. Il mio approccio è disegnare un collegamento, partendo da un nodo, pixel per pixel fino a raggiungere il nodo successivo (qui i nodi sono di colore diverso da quello dello sfondo), quindi accedendo al valore del pixel, voglio essere in grado di decidere il punto di intersezione del link e il nodo, che è in realtà la coordinata per disegnare la freccia-testa.Disegna una freccia su tela HTML5 tra due oggetti

Sarebbe bello, se potessi ottenere un aiuto con questo.

codice di esempio: http://jsfiddle.net/9tUQP/4/

Qui i quadrati verdi sono i nodi e la linea partendo dalla piazza di sinistra e di entrare in piazza a destra è il link. Voglio che la punta della freccia sia disegnata nel punto di intersezione del collegamento e nel quadrato corretto.

risposta

15

Ho creato un esempio che esegue questa operazione. Io uso Bresenham's Line Algorithm per percorrere la linea di pixel di tutta la tela e controllare l'alfa in ogni punto; ogni volta che attraversa un punto 'soglia', lo registro come candidato. Quindi utilizzo il primo e l'ultimo di tali punti per disegnare una freccia (con la punta della freccia correttamente ruotata).

Ecco l'esempio: http://phrogz.net/tmp/canvas_shape_edge_arrows.html

Refresh l'esempio per vedere un nuovo caso di test casuale. 'Fallisce' se hai un'altra 'forma' che si sovrappone già a uno dei punti finali. Un modo per risolverlo sarebbe quello di disegnare le forme prima su una tela bianca e quindi copiare il risultato (drawImage) nella tela finale.

Per Stack Overflow posteri (nel caso in cui il mio sito è giù) ecco il codice rilevante:

<!DOCTYPE html> 
<html><head> 
    <meta charset="utf-8"> 
    <title>HTML5 Canvas Shape Edge Detection (for Arrow)</title> 
    <style type="text/css"> 
    body { background:#eee; margin:2em 4em; text-align:center; } 
    canvas { background:#fff; border:1px solid #666 } 
    </style> 
</head><body> 
    <canvas width="800" height="600"></canvas> 
    <script type="text/javascript"> 
    var ctx = document.querySelector('canvas').getContext('2d'); 

    for (var i=0;i<20;++i) randomCircle(ctx,'#999'); 

    var start = randomDiamond(ctx,'#060'); 
    var end = randomDiamond(ctx,'#600'); 
    ctx.lineWidth = 2; 
    ctx.fillStyle = ctx.strokeStyle = '#099'; 
    arrow(ctx,start,end,10); 

    function arrow(ctx,p1,p2,size){ 
     ctx.save(); 

     var points = edges(ctx,p1,p2); 
     if (points.length < 2) return 
     p1 = points[0], p2=points[points.length-1]; 

     // Rotate the context to point along the path 
     var dx = p2.x-p1.x, dy=p2.y-p1.y, len=Math.sqrt(dx*dx+dy*dy); 
     ctx.translate(p2.x,p2.y); 
     ctx.rotate(Math.atan2(dy,dx)); 

     // line 
     ctx.lineCap = 'round'; 
     ctx.beginPath(); 
     ctx.moveTo(0,0); 
     ctx.lineTo(-len,0); 
     ctx.closePath(); 
     ctx.stroke(); 

     // arrowhead 
     ctx.beginPath(); 
     ctx.moveTo(0,0); 
     ctx.lineTo(-size,-size); 
     ctx.lineTo(-size, size); 
     ctx.closePath(); 
     ctx.fill(); 

     ctx.restore(); 
    } 

    // Find all transparent/opaque transitions between two points 
    // Uses http://en.wikipedia.org/wiki/Bresenham's_line_algorithm 
    function edges(ctx,p1,p2,cutoff){ 
     if (!cutoff) cutoff = 220; // alpha threshold 
     var dx = Math.abs(p2.x - p1.x), dy = Math.abs(p2.y - p1.y), 
      sx = p2.x > p1.x ? 1 : -1, sy = p2.y > p1.y ? 1 : -1; 
     var x0 = Math.min(p1.x,p2.x), y0=Math.min(p1.y,p2.y); 
     var pixels = ctx.getImageData(x0,y0,dx+1,dy+1).data; 
     var hits=[], over=null; 
     for (x=p1.x,y=p1.y,e=dx-dy; x!=p2.x||y!=p2.y;){ 
     var alpha = pixels[((y-y0)*(dx+1)+x-x0)*4 + 3]; 
     if (over!=null && (over ? alpha<cutoff : alpha>=cutoff)){ 
      hits.push({x:x,y:y}); 
     } 
     var e2 = 2*e; 
     if (e2 > -dy){ e-=dy; x+=sx } 
     if (e2 < dx){ e+=dx; y+=sy } 
     over = alpha>=cutoff; 
     } 
     return hits; 
    } 

    function randomDiamond(ctx,color){ 
     var x = Math.round(Math.random()*(ctx.canvas.width - 100) + 50), 
      y = Math.round(Math.random()*(ctx.canvas.height - 100) + 50); 
     ctx.save(); 
     ctx.fillStyle = color; 
     ctx.translate(x,y); 
     ctx.rotate(Math.random() * Math.PI); 
     var scale = Math.random()*0.8 + 0.4; 
     ctx.scale(scale,scale); 
     ctx.lineWidth = 5/scale; 
     ctx.fillRect(-50,-50,100,100); 
     ctx.strokeRect(-50,-50,100,100); 
     ctx.restore(); 
     return {x:x,y:y}; 
    } 

    function randomCircle(ctx,color){ 
     ctx.save(); 
     ctx.beginPath(); 
     ctx.arc(
     Math.round(Math.random()*(ctx.canvas.width - 100) + 50), 
     Math.round(Math.random()*(ctx.canvas.height - 100) + 50), 
     Math.random()*20 + 10, 
     0, Math.PI * 2, false 
    ); 
     ctx.fillStyle = color; 
     ctx.fill(); 
     ctx.lineWidth = 2; 
     ctx.stroke(); 
     ctx.restore(); 
    } 

    </script> 
</body></html> 
+0

link al mio lavoro: http://jsfiddle.net/emYhJ/ Puoi pls refactoring per adattarsi il mio codice –

+0

Grazie comunque .. Capito :) –

+0

@ allwyn.menezes perché vuoi disegnarlo attraverso il secondo cubo? Puoi aggiornare il tuo violino? – FutuToad