Как создать SVG дуги между двумя точками

123
26 марта 2022, 12:40

Я хочу соединить две точки SVG (например, центры двух окружностей), используя дуги. Если будет только одно соединение, то линия (<path>) будет прямой. Если есть два соединения, оба будут округлены и будут симметричными, таким образом:

Итак, на самом деле, есть несколько правил:

  1. Все должно быть симметрично относительно воображаемой линии, соединяющей две точки.

  2. Из первого пункта очевидно, что если число соединений:

    • нечётно: мы не отображаем прямую линию

    • четное: отображаем прямую линию

      1. Должно быть значение k, которое определяет расстояние между двумя соединениями между одинаковыми точками.

      2. Касательная линия, проходящая через середину эллиптической дуги, должна быть параллельной прямой линии, соединяющей две точки. И, очевидно, середина линии будет перпендикулярна касательной.

Я пытаюсь получить формулу для вычисления параметров 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>

Answer 1

Перевод ответа @r3mainer на вопрос EnSO: Create svg arcs between two points

Вы делаете жизнь очень сложной для себя, требуя использовать дуги окружностей.
Если вместо этого вы используете 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>

Источник: @r3mainer

Answer 2

Вот, вывел формулу для радиуса:

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>

Answer 3

Динамическое изменение связей при изменении позиций связанных блоков

Блоки можно перетаскивать мышкой и связи между блоками будут автоматически менять свою длину и форму кривых.

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>

Источник:

READ ALSO
Использование буфера трафарета (stencil buffer) в three.js

Использование буфера трафарета (stencil buffer) в three.js

Пытаюсь создать сцену с применением буфера трафарета, но к сожалению, по его использованию слишком мало информации и примеровПланирую по центру...

176
Обновление стейта в цикле React

Обновление стейта в цикле React

Есть такой компонент:

83
Процесс создания классов

Процесс создания классов

Кто-нибудь может пожалуйста объяснить более доходчивым образом, почему так происходит? Как я понимаю, в момент создания класса,не создания...

124
Как применить действие кнопки по очереди к каждой строке с одинаковыми классами?

Как применить действие кнопки по очереди к каждой строке с одинаковыми классами?

при нажатии кнопки нужно чт бы копировался майл под которым она находитсяСейчас все копи рую первое значение

190