Skip to content

Instantly share code, notes, and snippets.

@cool-Blue
Last active August 29, 2015 14:24
Show Gist options
  • Save cool-Blue/650894875bae9953e037 to your computer and use it in GitHub Desktop.
Save cool-Blue/650894875bae9953e037 to your computer and use it in GitHub Desktop.
svg path clipped by filter element

Problem with filter clipping

When a path element has instructions like this d="M28,46L28,23L77,23" to render two orthogonal lines, it works fine with the filter and the drop shadow rendered as expected, but, if the length of one of the lines is shorter than the corresponding dimension of the marker, a problem emerges: the path element, including the marker, start to get clipped by the filter.
I don't understand what's going on exactly, but it seems that the bounding box for the filter, which is a percentage of the path bounding box, collapses to zero height and this somehow clips the referencing path element. As soon as the path bounding box becomes zero, the problem disappears (at least it does in Chrome and Opera...).

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
svg {
outline: 1px solid black;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var width = 300,
height = 100,
constant = 10;
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var defs = svg.append("defs");
var markerW = 8, markerH = 6,
marker = defs.append('marker')
.attr('id', "triangle")
.attr('viewBox', "0 0 10 10")
.attr('refX', "0")
.attr('refY', 5)
.attr('markerUnits', 'strokeWidth')
.attr('markerWidth', markerW)
.attr('markerHeight', markerH)
.attr('orient', 'auto')
var path = marker.append('path')
.attr('d', "M 0 0 L 10 5 L 0 10 z")
// create filter with id #drop-shadow
// height=130% so that the shadow is not clipped
var filter = defs.append("filter")
.attr("id", "drop-shadow")
.attr({"height": "200%", "width": "200%", x: "-50%", y: "-50%"})
/*.style({opacity: 0.5})*/;
// SourceAlpha refers to opacity of graphic that this filter will be applied to
// convolve that with a Gaussian with standard deviation 3 and store result
// in blur
filter.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", 1)
.attr("result", "blur");
// translate output of Gaussian blur to the right and downwards with 2px
// store result in offsetBlur
var feOffset = filter.append("feOffset")
.attr("in", "blur")
.attr("dx", 2)
.attr("dy", 2)
.attr("result", "offsetBlur");
// overlay original SourceGraphic over translated blurred opacity by using
// feMerge filter. Order of specifying inputs is important!
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode")
.attr("in", "offsetBlur")
feMerge.append("feMergeNode")
.attr("in", "SourceGraphic");
var connector = d3.svg.line().interpolate("linear")
.x(function (d) {
return Math.round(d[0])
})
.y(function (d) {
return Math.round(d[1])
});
function linkPath(d) {
var x1 = d[0][0], y1 = d[0][1],
x2 = d[1][0], y2 = d[1][1];
return connector([[x1, y1], [x1, y2], [x2, y2]]);
}
var link = svg.selectAll('.link')
.data([[[10, 40], [200, 40]]])
.enter()
.append('path').attr("class", "link")
.attr("stroke-width", "2")
.attr('marker-end', 'url(#triangle)')
.attr('stroke', 'black')
.attr("fill", "none")
.style("filter", "url(#drop-shadow)")
.attr("d", linkPath)
function start() {
var t = 3000;
link.data([[[10, 60], [200, 40]]])
.transition().delay(t / 3).duration(t).ease("linear")
.attr("d", linkPath)
.each("end", function () {
link.data([[[10, 40], [200, 40]]])
.transition().duration(t).ease("linear")
.attr("d", linkPath)
.each("end", function () {
link.data([[[10, 20], [200, 40]]])
.transition().delay(t / 3).duration(t).ease("linear")
.attr("d", linkPath)
.each("end", function () {
link.data([[[10, 40], [200, 40]]])
.transition().duration(t).ease("linear")
.attr("d", linkPath)
.each("end", start)
})
})
})
}
;
start();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment