Вам просто нужно приблизить свой знаменатель к 2,0, так как когда он равен 2,0, это совершенно прямая линия: https://jsfiddle.net/my7bx50p/1/
Так что я выбрал 2,03 и 1,97, и это дает вам много "более мягкие" кривые Надеюсь, это поможет.
function pointsToPath(from, to, invertArc) {
var arcPointX = (from.x + to.x) / (invertArc ? 2.03 : 1.97),
arcPointY = (from.y + to.y) / (invertArc ? 2.03 : 1.97);
return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY + ' ' + to.x + ' ' + to.y;
}
ОБНОВЛЕНИЕ:
Я пытался сосредоточиться только на математика: https://jsfiddle.net/9gkvhfuL/1/
Я думаю, что математика теперь верна:
Которые обратно к реальный пример: https://jsfiddle.net/my7bx50p/6/
Дает, я полагаю, желаемый результат:):
Из кода (https://jsfiddle.net/my7bx50p/6/):
function pointsToPath(from, to, invertArc) {
var centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
var slope = (to.x - from.x) / (to.y - from.y);
var invSlope = -1 / slope;
var distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );
if (Math.abs(slope) > Math.abs(invSlope) ){
//then we should offset in the y direction
var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
var min_slope = Math.min( Math.abs(slope), Math.abs(invSlope) );
var final_slope = Math.max(min_slope, 1);
var offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];
//console.log(centerPoint, slope, invSlope, distance);
var arcPointX = offsetCenter[0], //(from.x + to.x) / (invertArc ? 2.03 : 1.97),
arcPointY = offsetCenter[1] //(from.y + to.y) / (invertArc ? 2.03 : 1.97);
} else{ //invSlope <= slope
//then we should offset in the x direction
var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
var min_slope = Math.min( Math.abs(slope), Math.abs(invSlope) );
var final_slope = Math.max(min_slope, 1);
var offsetCenter = [centerPoint[0] + offset, centerPoint[1] + (offset * (1/invSlope))];
//console.log(centerPoint, slope, invSlope, distance);
var arcPointX = offsetCenter[0], //(from.x + to.x) / (invertArc ? 2.03 : 1.97),
arcPointY = offsetCenter[1] //(from.y + to.y) / (invertArc ? 2.03 : 1.97);
}
return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
' ' + to.x + ' ' + to.y;
}
ОБНОВЛЕНИЕ 2: (чтобы попытаться объяснить математику и очистить код)
Проверьте математическую скрипку: https://jsfiddle.net/alexander_L/dcormfxy/53/
Черная линия solid между двумя точками - это прямая линия между ними и имеет также соответствующий уклон (используется в коде позже). Я также нарисовал центральную точку на каждой линии. Затем я нарисовал обратный наклон в виде пунктирной линии (также используется в коде). Обратный наклон по определению перпендикулярен наклону и связан с invSlope = -1/slope
. Исходя из этого, мы теперь настроены на поиск перпендикулярных точек слева или справа от центральной точки, которые станут центром наших симметричных дуг. Мы делаем это, сначала выясняя, является ли наклон больше, чем обратный наклон, или если обратный наклон больше, чем наклон (абсолютные значения). Это необходимо только потому, что когда у нас совершенно горизонтальная или совершенно вертикальная линия, наклон равен нулю и не определен соответственно, и тогда наша математика не работает. . 1064 * Давайте подумаем о линии C from : {x: 40, y: 40}, to : {x: 220, y: 40}
slope = (y2 - y1) / (x2 - x1)
клон = (40 - 40) / (220 - 40)
slope = 0/180
slope = 0
invSlope = -1 / slope
invSlope = undefined
Вот почему нам нужно иметь два случая (if if) в коде, так как всякий раз, когда мы получаем наклон или invSlope как неопределенный, математика не будет работать. Так что теперь, хотя наклон равен нулю, он больше, чем invSlope (undefined). (обратите внимание, что SVG вверх ногами по сравнению с обычными графиками и тем, как мы о них думаем, поэтому вам нужно помнить об этом свой мозг, иначе легко потеряться)
Так что теперь мы можем сместить центральную точку в направление у, а затем выяснить, сколько мы должны сместить в направлении х. Если бы у вас была линия с наклоном 1, то вы бы сместили ее в направлениях x и y, потому что наклон линии равен 1 (линия образует угол 45 градусов с осью x) и поэтому перемещается перпендикулярно от этой линии достигается просто перемещением, например, 5 в направлении x и -5 в направлении y.
К счастью, с этим краевым случаем (наклон = 0), тогда мы просто перемещаемся в y -направление и смещение по оси x = 0. Посмотрите на строку C в математическом примере, и вы можете увидеть, что я имею в виду, чтобы двигаться перпендикулярно, мы просто перемещаем либо положительное, либо отрицательное направление y от центральной точки. Из кода:
offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];
Таким образом, как я уже сказал, мы смещаемся в направлении Y от центральной точки, и термин + (offset * (1/slope))
здесь будет равен нулю, потому что 1 / наклон не определен , Мы можем выбрать смещение «влево» или «вправо» с помощью аргумента функции invertArc
, который используется в этой строке: var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
, что в основном означает смещение положительного или отрицательного направления от центральной точки на величину, равную двум значениям квадрата. root расстояния между точками. Я установил в два раза квадрат root расстояния между точками, потому что это дает нам центр смещения нашего ar c, который дает одинаково мягкие кривые для всех линий, как коротких, так и длинных.
Теперь давайте подумайте о линии A from : {x: 40, y: 40}, to : {x: 320, y: 360}
slope = (y2 - y1) / (x2 - x1)
наклон = (360 - 40) / (320 - 40)
наклон = 320/280
наклон = 1,143
invSlope = -1 / наклон
invSlope = -0,875
Окончательный исправленный код и реальный пример здесь https://jsfiddle.net/alexander_L/o43ka9u5/4/:
function pointsToPath(from, to, invertArc) {
var centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
var slope = (to.x - from.x) / (to.y - from.y);
var invSlope = -1 / slope;
var distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );
var arcPointX = 0;
var arcPointY = 0;
var offset = 0;
var offsetCenter = 0;
if (Math.abs(slope) > Math.abs(invSlope) ){
//then we should offset in the y direction (then calc. x-offset)
offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];
arcPointX = offsetCenter[0]
arcPointY = offsetCenter[1]
} else{ //invSlope >= slope
//then we should offset in the x direction (then calc. y-offset)
offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
offsetCenter = [centerPoint[0] + offset, centerPoint[1] + (offset * (1/invSlope))];
arcPointX = offsetCenter[0]
arcPointY = offsetCenter[1]
}
return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
' ' + to.x + ' ' + to.y;
}
ОБНОВЛЕНИЕ 3:
Я понял, как устранить необходимость в операторе управления потоком / переключением if else, используя вместо этого тригонометрию. Я надеюсь, что мой набросок поможет объяснить логику c, вы также можете прочитать кое-что (https://study.com/academy/lesson/sohcahtoa-definition-example-problems-quiz.html) и c. как я изо всех сил пытаюсь объяснить здесь кратко (а я уже пишу здесь эссе :), поэтому не буду объяснять SOH CAH TOA et c.)
Таким образом код основной функции выглядит следующим образом (только Math - https://jsfiddle.net/alexander_L/dcormfxy/107/) (полный пример - https://jsfiddle.net/alexander_L/o43ka9u5/6/):
function pointsToPath(from, to, invertArc) {
const centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
const slope = (to.y - from.y) / (to.x - from.x);
const invSlope = -1 / slope;
const distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );
const offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
const angle = Math.atan(slope);
//Math.cos(angle) = offsetY/offset;
//Math.sin(angle) = offsetX/offset;
const offsetY = Math.cos(angle)*offset;
const offsetX = Math.sin(angle)*offset;
//if slope = 0 then effectively only offset y-direction
const offsetCenter = [centerPoint[0] - offsetX, centerPoint[1] + offsetY];
const arcPointX = offsetCenter[0]
const arcPointY = offsetCenter[1]
return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
' ' + to.x + ' ' + to.y;
}
I считаю, что этот код более элегантный, чистый, надежный и математически обоснованный :) Спасибо также A sh за советы по использованию Const & Let по сравнению с var.
Это также дает конечный результат: