Ivan
Ivan

Reputation: 40648

<svg> path create a curvy line to link two points

I'm a currently trying out SVG paths with js and would like to link two points with a curvy line. Here's my progress so far:

// draw a curvy line between point (startX,startY) and point (endX,endY)
function drawCurve(startX, startY, endX, endY) {

    // exemple of a path: M318,345 L330,345 C450,345 380,124 504,124 L519,124

    // M
    var AX = startX;
    var AY = startY;

    // L
    var BX = Math.abs(endX - startX) * 0.05 + startX;
    var BY = startY;
  
    // C
    var CX = (endX - startX) * 0.66 + startX;
    var CY = startY;
    var DX = (endX - startX) * 0.33 + startX;
    var DY = endY;
    var EX = - Math.abs(endX - startX) * 0.05 + endX;
    var EY = endY;

    // L
    var FX = endX;
    var FY = endY;

    // [DEBUGGING] add all the colored points for testing
    document.getElementById('pointA').setAttribute("cx", AX);
    document.getElementById('pointA').setAttribute("cy", AY);
    document.getElementById('pointB').setAttribute("cx", BX);
    document.getElementById('pointB').setAttribute("cy", BY);
    document.getElementById('pointC').setAttribute("cx", CX);
    document.getElementById('pointC').setAttribute("cy", CY);
    document.getElementById('pointD').setAttribute("cx", DX);
    document.getElementById('pointD').setAttribute("cy", DY);
    document.getElementById('pointE').setAttribute("cx", EX);
    document.getElementById('pointE').setAttribute("cy", EY);
    document.getElementById('pointF').setAttribute("cx", FX);
    document.getElementById('pointF').setAttribute("cy", FY);

    // setting up the path string
    var path = 'M' + AX + ',' + AY;
    path += ' L' + BX + ',' + BY;
    path +=  ' ' + 'C' + CX + ',' + CY;
    path += ' ' + DX + ',' + DY;
    path += ' ' + EX + ',' + EY;
    path += ' L' + FX + ',' + FY;

    // [DEBUGGING] display the path string
    console.log('path is '+path);

    // applying the new path to the element
    document.getElementById('myPath').setAttribute("d", path);

}

drawCurve(200,400, 519,124);
<svg height="1000" width="1000">

    <path id="myPath" d="" stroke="blue" stroke-width="5" fill="none" />

    <g stroke="black" stroke-width="3" fill="black">
        <circle id="pointA" cx="318" cy="345" r="1" />
        <circle id="pointB" cx="330" cy="345" r="1" />
    </g>
    <g stroke="red" stroke-width="3" fill="red">
        <circle id="pointC" cx="450" cy="345" r="1" />
    </g>
    <g stroke="green" stroke-width="3" fill="green">
        <circle id="pointD" cx="380" cy="124" r="1" />
    </g>
    <g stroke="black" stroke-width="3" fill="black">
        <circle id="pointE" cx="504" cy="124" r="1" />
        <circle id="pointF" cx="519" cy="124" r="1" />
    </g>

</svg>

CodePen version here

It's working but the problem I found is the following, when I but point (startX,startY) on the right of (endX,endY) I don't the result I want. Here's what I have: enter image description here And what I would like to have: enter image description here

So it seems like I need to add another curveTo in the path.

Also I've found this CodePen that is working.

Upvotes: 2

Views: 2575

Answers (1)

Paul LeBeau
Paul LeBeau

Reputation: 101820

Instead of using the signed difference between startX and endX to calculate the position of the bezier control points, use the absolute value instead.

Also, to get the second control point, subtract a third of the distance from endX rather than adding two-thirds to startX.

var CX = startX + Math.abs(endX - startX) * 0.33;
var DX = endX - Math.abs(endX - startX) * 0.33;

// draw a curvy line between point (startX,startY) and point (endX,endY)
function drawCurve(startX, startY, endX, endY) {

    // exemple of a path: M318,345 L330,345 C450,345 380,124 504,124 L519,124

    // M
    var AX = startX;
    console.log(AX);
    var AY = startY;

    // L
    var BX = Math.abs(endX - startX) * 0.05 + startX;
    var BY = startY;
  
    // C
    var CX = startX + Math.abs(endX - startX) * 0.33;
    var CY = startY;
    var DX = endX - Math.abs(endX - startX) * 0.33;
    var DY = endY;
    var EX = - Math.abs(endX - startX) * 0.05 + endX;
    var EY = endY;

    // L
    var FX = endX;
    var FY = endY;

    // [DEBUGGING] add all the colored points for testing
    document.getElementById('pointA').setAttribute("cx", AX);
    document.getElementById('pointA').setAttribute("cy", AY);
    document.getElementById('pointB').setAttribute("cx", BX);
    document.getElementById('pointB').setAttribute("cy", BY);
    document.getElementById('pointC').setAttribute("cx", CX);
    document.getElementById('pointC').setAttribute("cy", CY);
    document.getElementById('pointD').setAttribute("cx", DX);
    document.getElementById('pointD').setAttribute("cy", DY);
    document.getElementById('pointE').setAttribute("cx", EX);
    document.getElementById('pointE').setAttribute("cy", EY);
    document.getElementById('pointF').setAttribute("cx", FX);
    document.getElementById('pointF').setAttribute("cy", FY);

    // setting up the path string
    var path = 'M' + AX + ',' + AY;
    path += ' L' + BX + ',' + BY;
    path +=  ' ' + 'C' + CX + ',' + CY;
    path += ' ' + DX + ',' + DY;
    path += ' ' + EX + ',' + EY;
    path += ' L' + FX + ',' + FY;

    // [DEBUGGING] display the path string
    console.log(path);

    // applying the new path to the element
    document.getElementById('myPath').setAttribute("d", path);

}

//drawCurve(200,400, 519,124);
drawCurve(519,124, 200,400);
<svg height="1000" width="1000">

    <path id="myPath" d="" stroke="blue" stroke-width="5" fill="none" />

    <g stroke="black" stroke-width="3" fill="black">
        <circle id="pointA" cx="318" cy="345" r="1" />
        <circle id="pointB" cx="330" cy="345" r="1" />
    </g>
    <g stroke="red" stroke-width="3" fill="red">
        <circle id="pointC" cx="450" cy="345" r="1" />
    </g>
    <g stroke="green" stroke-width="3" fill="green">
        <circle id="pointD" cx="380" cy="124" r="1" />
    </g>
    <g stroke="black" stroke-width="3" fill="black">
        <circle id="pointE" cx="504" cy="124" r="1" />
        <circle id="pointF" cx="519" cy="124" r="1" />
    </g>

</svg>

Upvotes: 5

Related Questions