Создание svg-дуг между двумя точками
Я хочу подключить две точки SVG (например, центры из двух окружностей), используя дуги. Если есть только одно соединение, линия (<path>
) будет прямой. Если есть два соединения, оба будут округлены и будут симметричными, таким образом:
![]()
Итак, на самом деле существует несколько правил:
- Все должно быть симметричным для воображаемой линии, соединяющей две точки.
-
Из 1 видно, что если количество соединений:
- нечетный: мы не показываем прямую линию
- even: мы показываем прямую линию
-
Должно быть значение k
, которое определяет расстояние между двумя соединениями между одними и теми же точками.
-
Тангенс, проходящий через середину эллиптической дуги, должен быть параллелен прямой линии, соединяющей две точки. И, очевидно, середина линии будет перпендикулярна касательной.
![]()
Я пытаюсь получить формулу для вычисления параметров A
в элементе <path>
.
То, что я делал до сих пор:
<path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent"/>
-
M100 100
ясно: что отправная точка (переход на 100 100)
- Последние два числа также ясны. Путь заканчивается на
300,100
- Я также заметил, что если я поставлю
0
вместо 20
, я получаю прямую линию.
- Если я заменил
1,0
на 1,1
, путь будет перевернут.
Я не знаю, как вычислить параметры A. Я читал документы, но мне все еще не ясно. Как рассчитать эти значения?
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>
Ответы
Ответ 1
Вы делаете жизнь очень трудной для себя, требуя круговых дуг.
Если вместо этого вы используете квадратные кривые, то геометрия становится очень простой - просто смещайте центральную координату X на половину разницы в координатах Y и наоборот.
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
Вот решение, которое использует дуги, как просили, а не квадратичные кривые.
// 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
}