Reputation: 40648
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:
And what I would like to have:
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
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