web-dev-qa-db-ja.com

2点間に湾曲したSVGパスを作成するにはどうすればよいですか?

2つの円の中心間の対称曲線を描く必要があります。

<svg>
    <circle class="spot" id="au" cx="1680" cy="700" r="0"></circle>
    <circle class="spot" id="sl" cx="1425" cy="525" r="0"></circle>

    <line id="line1" stroke-width="2" stroke="red"/>
</svg>

これは私がこれまでに書いたコードです。 <line>要素は曲線パスに置き換える必要があります

function drawNow() {
    let point1X = document.getElementById("au").getAttribute("cx");
    let point1Y = document.getElementById("au").getAttribute("cy");
    let point2X = document.getElementById("sl").getAttribute("cx");
    let point2Y = document.getElementById("sl").getAttribute("cy");

    let line1 = document.getElementById("line1");
    line1.setAttribute("x1", point1X);
    line1.setAttribute("y1", point1Y);
    line1.setAttribute("x2", point2X);
    line1.setAttribute("y2", point2Y);
}
5
WenukaGTX

SVGの2次曲線でおそらく十分でしょう。それを描くには、(持っている)終点と曲線を決定する制御点が必要です。

対称曲線を作成するには、制御点が端点間の線の垂直二等分線上にある必要があります。少しの数学がそれを見つけるでしょう。

だから、2つのポイントから...

Two points to join

あなたはに行くことができます

Points joined with curve

のコードで

<!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
                svg { background-color: bisque; }
                .spot { fill: blue; }
                .spot2 { fill: red; }
        </style>
        <script>
                function x() {
                        var p1x = parseFloat(document.getElementById("au").getAttribute("cx"));
                        var p1y = parseFloat(document.getElementById("au").getAttribute("cy"));
                        var p2x = parseFloat(document.getElementById("sl").getAttribute("cx"));
                        var p2y = parseFloat(document.getElementById("sl").getAttribute("cy"));
    
                        // mid-point of line:
                        var mpx = (p2x + p1x) * 0.5;
                        var mpy = (p2y + p1y) * 0.5;
    
                        // angle of perpendicular to line:
                        var theta = Math.atan2(p2y - p1y, p2x - p1x) - Math.PI / 2;
    
                        // distance of control point from mid-point of line:
                        var offset = 30;
    
                        // location of control point:
                        var c1x = mpx + offset * Math.cos(theta);
                        var c1y = mpy + offset * Math.sin(theta);
    
                        // show where the control point is:
                        var c1 = document.getElementById("cp");
                        c1.setAttribute("cx", c1x);
                        c1.setAttribute("cy", c1y);
    
                        // construct the command to draw a quadratic curve
                        var curve = "M" + p1x + " " + p1y + " Q " + c1x + " " + c1y + " " + p2x + " " + p2y;
                        var curveElement = document.getElementById("curve");
                        curveElement.setAttribute("d", curve);
                }
        </script>
    </head>
    <body>
        <svg width="240" height="160">
                <circle id="au" class="spot" cx="200" cy="50" r="4"></circle>
                <circle id="sl" class="spot" cx="100" cy="100" r="4"></circle>
                <circle id="cp" class="spot2" cx="0" cy="0" r="4"></circle>
                <path id="curve" d="M0 0" stroke="green" stroke-width="4" stroke-linecap="round" fill="transparent"></path>
        </svg>
        <button type="button" onclick="x();">Click</button>
    </body>
    </html>

曲線を逆方向に移動させたい場合は、offsetの符号を変更します。

ES6準拠のブラウザーを使用している場合は、少しきちんとしたコードに 文字列補間 を使用できます。

var curve = `M${p1x} ${p1y} Q${c1x} ${c1y} ${p2x} ${p2y}`;

コントロールポイントを表示する必要はありません。これは、コントロールポイントがどこにあるかを確認し、曲線がコントロールポイントを通過しないことを示すためです。

注:atan2を使用する代わりに、ポイント間の線の勾配の負の逆数を計算することもできますが、勾配がゼロの場合は厄介で、勾配がゼロに近いと非常に不正確な結果が生成される可能性があります。

16
Andrew Morton