Create svg arcs between two points

江枫思渺然 提交于 2019-12-04 08:56:10

问题


I want to connect two SVG points (e.g. the centers of two circles) using arcs. If there is only one connection, the line (<path>) will be straight. If there are two connections, both will be rounded and will be symmetrical, this way:

So, in fact, there are few rules:

  1. Everything should be symmetrical to to the imaginary line that connects the two points.
  2. From 1, it's obvious that if the number of connections is:

    • odd: we do not display the straight line
    • even: we display the straight line
  3. There should be a value k which defines the distance between two connections between same points.

  4. The tangent that goes through the middle of the elliptical arc should be parallel with the straight line that connects the two points. And obviously, the middle of the line will be perpendicular to the tangent.

I'm struggling to get a formula to calculate the A parameters in the <path> element.

What I did until now is:

<path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent"/>
  • M100 100 is clear: that's the starting point (move to 100,100)
  • Last two numbers are also clear. The path ends in 300,100
  • I also saw that if I put 0 instead of 20, I obtain a straight line.
  • If I replace 1,0 with 1,1, the path is flipped.

What I don't know is how to calculate the A parameters. I read the docs, but the imagine is still unclear to me. How to calculate these values?

svg {
  width: 100%;
  height: 100%;
  position: absolute;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>

<body>
  <?xml version="1.0" standalone="no" ?>

    <svg version="1.1" xmlns="http://www.w3.org/2000/svg">
      <!-- Connect A(100,100) with B(300, 100) -->
      <path d="M100 100, A50,0 0 1,0 300,100" stroke="black" fill="transparent" />
      <path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent" />
      <path d="M100 100, A50,20 0 1,1 300,100" stroke="black" fill="transparent" />
      <path d="M100 100, A50,30 0 1,0 300,100" stroke="black" fill="transparent" />
      <path d="M100 100, A50,30 0 1,1 300,100" stroke="black" fill="transparent" />
      
      <!-- A(100, 100) B(300, 400) -->
      <path d="M100 100, A50,0 57 1,0 300,400" stroke="black" fill="transparent" />
      <path d="M100 100, A50,20 57 1,0 300,400" stroke="black" fill="transparent" />
      <path d="M100 100, A50,20 57 1,1 300,400" stroke="black" fill="transparent" />
  </svg>
</body>

</html>

I'm using SVG.js to create the paths.


回答1:


You're making life very difficult for yourself by requiring circular arcs.

If you use quadratic curves instead, then the geometry becomes very simple — just offset the central X coordinate by half the difference in Y coordinates, and vice versa.

function arc_links(dwg,x1,y1,x2,y2,n,k) {
  var cx = (x1+x2)/2;
  var cy = (y1+y2)/2;
  var dx = (x2-x1)/2;
  var dy = (y2-y1)/2;
  var i;
  for (i=0; i<n; i++) {
    if (i==(n-1)/2) {
      dwg.line(x1,y1,x2,y2).stroke({width:1}).fill('none');
    }
    else {
      dd = Math.sqrt(dx*dx+dy*dy);
      ex = cx + dy/dd * k * (i-(n-1)/2);
      ey = cy - dx/dd * k * (i-(n-1)/2);
      dwg.path("M"+x1+" "+y1+"Q"+ex+" "+ey+" "+x2+" "+y2).stroke({width:1}).fill('none');
    }
  }
}

function create_svg() {
  var draw = SVG('drawing').size(300, 300);
  arc_links(draw,50,50,250,50,2,40);
  arc_links(draw,250,50,250,250,3,40);
  arc_links(draw,250,250,50,250,4,40);
  arc_links(draw,50,250,50,50,5,40);
  draw.circle(50).move(25,25).fill('#fff').stroke({width:1});
  draw.circle(50).move(225,25).fill('#fff').stroke({width:1});
  draw.circle(50).move(225,225).fill('#fff').stroke({width:1});
  draw.circle(50).move(25,225).fill('#fff').stroke({width:1});
}

create_svg();
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.3.2/svg.min.js"></script>
<div id="drawing"></div>



回答2:


Here is a solution that uses arcs, as asked for, rather than quadratic curves.

// Internal function
function connectInternal(x1,y1,x2,y2,con){
 var dx=x2-x1
 var dy=y2-y1
 var dist=Math.sqrt(dx*dx+dy*dy)
 if(dist==0 || con==0){
  return "M"+x1+","+y1+"L"+x2+","+y2
 }
 var xRadius=dist*0.75
 var yRadius=dist*0.3*(con*0.75)
 var normdx=dx/dist
 if(normdx<-1)normdx=-1
 if(normdx>1)normdx=1
 var angle=Math.acos(normdx)*180/Math.PI
 if(x1>x2){
  angle=-angle
 }
 return "M"+x1+","+y1+"A"+xRadius+","+yRadius+","+
   angle+",00"+x2+","+y2+
   "M"+x1+","+y1+"A"+xRadius+","+yRadius+","+
   angle+",01"+x2+","+y2
}

// Returns an SVG path that represents
// "n" connections between two points.
function connect(x1,y1,x2,y2,n){
 var ret=""
 var con=n
 if(con%2==1){
  ret+=connectInternal(x1,y1,x2,y2,con)
  con-=1
 }
 for(var i=2;i<=con;i+=2){
  ret+=connectInternal(x1,y1,x2,y2,i)
 }
 return ret
}


来源:https://stackoverflow.com/questions/31804392/create-svg-arcs-between-two-points

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!