Как создать SVG дуги между двумя точками
Я хочу соединить две точки SVG (например, центры двух окружностей), используя дуги.
Если будет только одно соединение, то линия (<path>) будет прямой. Если есть два соединения, оба будут округлены и будут симметричными, таким образом:
Итак, на самом деле, есть несколько правил:
Все должно быть симметрично относительно воображаемой линии, соединяющей две точки.
Из первого пункта очевидно, что если число соединений:
нечётно: мы не отображаем прямую линию
четное: отображаем прямую линию
Должно быть значение k, которое определяет расстояние между двумя соединениями между одинаковыми точками.
Касательная линия, проходящая через середину эллиптической дуги, должна быть параллельной прямой линии, соединяющей две точки. И, очевидно, середина линии будет перпендикулярна касательной.
Я пытаюсь получить формулу для вычисления параметров A в элементе <path>.
Что я делал до сих пор:
<path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent"/>
M100 100понятно, что это координаты стартовой точки (move to 100,100)- Последние две цифры также ясны,- конец
pathимеет координаты300,100 - Я также понял, что если я поставлю
0вместо20, то я получу прямую линию. - Если я заменю
1,0на1,1, путь будет перевернут.
То, что я не знаю, так это, как рассчитать параметры А. Я читаю документы, но не могу ясно представить, как рассчитать эти значения?
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>
Ответы (3 шт):
Вы делаете жизнь очень сложной для себя, требуя использовать дуги окружностей.
Если вместо этого вы используете quadratic curves, то геометрия становится очень простой - просто сместите центральную координату 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>
Вот, вывел формулу для радиуса:
let r = (d, x) => 0.125*d*d/x + x/2;
d - расстояние между точками
x - расстояние между дугами
Это следствие теоремы Пифагора:
на этой картинке а это расстояние между точками поделенное на 2.
let r = (d, x) => !x?1e10:0.125*d*d/x + x/2;
upd();
function upd() {
let n = +count.value;
let s = +step.value/10;
let x1 = c1.getAttribute('cx'), y1 = c1.getAttribute('cy');
let x2 = c2.getAttribute('cx'), y2 = c2.getAttribute('cy');
let dx = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
paths.innerHTML = [...Array(n)].map((_, i) => [
n%2&&i===n-1?0:1+parseInt(i/2),
i%2
]).map(i => `<path d="${[
'M', x1, y1,
'A', r(dx, s*i[0]), r(dx, s*i[0]), 0, 0, i[1], x2, y2
].join(' ')}"></path>`).join('');
}
<input id="count" type="range" min=1 max=9 value=5 oninput=upd() >
<input id="step" type="range" min=1 max=200 value=100 oninput=upd() >
<svg viewbox=0,0,300,100 stroke=red fill=none >
<circle id=c1 r=10 cx=50 cy=60></circle>
<circle id=c2 r=10 cx=250 cy=40></circle>
<g id=paths></g>
</svg>
Динамическое изменение связей при изменении позиций связанных блоков
Блоки можно перетаскивать мышкой и связи между блоками будут автоматически менять свою длину и форму кривых.
var divA = document.querySelector("#a");
var divB = document.querySelector("#b");
var arrowLeft = document.querySelector("#arrowLeft");
var arrowRight = document.querySelector("#arrowRight");
var drawConnector = function() {
var posnALeft = {
x: divA.offsetLeft - 8,
y: divA.offsetTop + divA.offsetHeight / 2
};
var posnARight = {
x: divA.offsetLeft + divA.offsetWidth + 8,
y: divA.offsetTop + divA.offsetHeight / 2
};
var posnBLeft = {
x: divB.offsetLeft - 8,
y: divB.offsetTop + divA.offsetHeight / 2
};
var posnBRight = {
x: divB.offsetLeft + divB.offsetWidth + 8,
y: divB.offsetTop + divA.offsetHeight / 2
};
var dStrLeft =
"M" +
(posnALeft.x ) + "," + (posnALeft.y) + " " +
"C" +
(posnALeft.x - 100) + "," + (posnALeft.y) + " " +
(posnBLeft.x - 100) + "," + (posnBLeft.y) + " " +
(posnBLeft.x ) + "," + (posnBLeft.y);
arrowLeft.setAttribute("d", dStrLeft);
var dStrRight =
"M" +
(posnBRight.x ) + "," + (posnBRight.y) + " " +
"C" +
(posnBRight.x + 100) + "," + (posnBRight.y) + " " +
(posnARight.x + 100) + "," + (posnARight.y) + " " +
(posnARight.x ) + "," + (posnARight.y);
arrowRight.setAttribute("d", dStrRight);
};
$("#a, #b").draggable({
drag: function(event, ui) {
drawConnector();
}
});
setTimeout(drawConnector, 250);
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#instructions {
position: fixed;
left: 50%;
}
#a, #b {
color: white;
text-align: center;
padding: 10px;
position: fixed;
width: 100px;
height: 20px;
left: 100px;
}
#a {
background-color: blue;
top: 20px;
}
#b {
background-color: red;
top: 180px;
}
<p id="instructions">Нажмите и перетащите любой div, чтобы увидеть непроизвольную установку стрелок.</p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="3" refY="5"
markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<g fill="none" stroke="black" stroke-width="2" marker-end="url(#arrowhead)">
<path id="arrowLeft"/>
<path id="arrowRight"/>
</g>
</svg>
<div id="a">Div 1</div>
<div id="b">Div 2</div>


