I try to draw thick Bezier lines (for a custom Sankey diagram).
I use SVG Paths, with Bezier curves in the form of C x1 y1, x2 y2, x y
. I use stroke
rather than fill
, so that they have constant width (and can represent flows).
It works very well if the lines are thin or if the vertical difference is relatively low. However, if they are very thick, I get some nasty artifacts (looking like horns) - see the bottom right curve from the picture below:
Source: http://jsfiddle.net/stared/83jr5fub/
Is there a way to avoid artifacts, i.e.:
x1
or right of x
,stroke-width
?You can extend an open Bézier path. To do so, briefly click one of the path's two end points with the Bézier tool while holding down the SHIFT key. You can then add further Bézier points by clicking. Bézier paths can be connected to other Bézier paths and also to lines, rectangles, polygons and ellipses.
You can also close a curve while in the process of drawing it by Ctrl + Shift + clicking the start point while the Bezier Curve Tool is active.
I think that he best solution in your case (with the given path), is to make your path closed, and use its fill property.
To do this, you'll have to make a lineTo(0, strokeWidth)
at the end of your BezierCurveTo
, and then to redraw the bezierCurve in the other way :
var svg = d3.select("#chart");
var data = [
{t: 5, dy: 10},
{t: 5, dy: 20},
{t: 5, dy: 40},
{t: 20, dy: 10},
{t: 20, dy: 20},
{t: 20, dy: 40},
{t: 50, dy: 10},
{t: 50, dy: 20},
{t: 50, dy: 40},
];
var ctrl = 10;
var dx = 40;
var spacing = 100;
var colors = d3.scale.category10();
svg
.attr("width", 4 * spacing)
.attr("height", 4 * spacing);
svg.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("d", function (d, i) {
var x1 = spacing + spacing * (i % 3);
var y1 = spacing + spacing * Math.floor(i / 3);
return "M" + x1 + "," + y1 +
"c" + ctrl + "," + 0 +
" " + (dx - ctrl) + "," + d.dy +
" " + dx + "," + d.dy +
// move down for the wanted width
"l" + (0) + "," + (d.t) +
// negate all values
"c" + (ctrl * -1) + "," + 0 +
" " + ((dx - ctrl) * -1) + "," + (d.dy * -1) +
" " + (dx * -1) + "," + (d.dy * -1);
})
.style("fill", colors(0))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="chart"></svg>
And since an animation worth more than 10 thousand words here is one showing what was happening and why it can't be called a browser bug :
@keyframes dash {
from {
stroke-dashoffset: -10%;
}
to {
stroke-dashoffset: 90%;
}
}
@-webkit-keyframes dash {
from {
stroke-dashoffset: -10%;
}
to {
stroke-dashoffset: 90%;
}
}
#dashed{
animation : dash 12s linear infinite;
}
<svg height="200" width="200" id="chart" viewBox="290 260 100 100">
<path id="dashed" style="fill: none; stroke: rgb(31, 119, 180); stroke-width: 50; stroke-dasharray: 3, 3;" d="M300,300c10,0 30,40 40,40"></path>
<path style="fill: none; stroke: black;" d="M300,300c10,0 30,40 40,40">
</path></svg>
Kaiido gave an excellent and complete answer for why the SVG-path with thick stroke-width are displayed with artifacts and how to avoid this. I'll try to provide a bit more info that is specific to D3.js Sankey diagrams, as I was recently facing the same problem as Piotr Migdal.
(from Sankey.js in this Sankey example, which is similar to the example Piotr Migdal mentioned)
// regular forward node
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3.interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0 = d.source.y + d.sy + d.dy / 2,
y1 = d.target.y + d.ty + d.dy / 2;
return "M" + x0 + "," + y0
+ "C" + x2 + "," + y0
+ " " + x3 + "," + y1
+ " " + x1 + "," + y1;
// regular forward node
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3.interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0 = d.source.y + d.sy,
y1 = d.target.y + d.ty;
return "M" + x0 + "," + y0
+ "C" + x2 + "," + y0
+ " " + x3 + "," + y1
+ " " + x1 + "," + y1
// move down for the wanted width
+ "l" + 0 + "," + d.dy
// draw another path below mirroring the top
+ "C" + x3 + "," + (y1 + d.dy)
+ " " + x2 + "," + (y0 + d.dy)
+ " " + x0 + "," + (y0 + d.dy);
Then you'll also need to change your css:
and remove any D3 code that sets stroke-width of HTML elements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With