I have the following simple example, When the line extends outside the rectangle, I want to clip it. I already have the rectangle used as an outline, what is a simple way to the same rectangle as a clipping path? My current approach using id
is ignored. This related question has an answer but it requires creating the clip area separately. I'd like to re-use my info rather than repeat nearly the same info.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src = "http://d3js.org/d3.v3.min.js"> </script>
<script>
var margin = {top: 100, right: 20, bottom: 20, left: 20},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var xdata = d3.range(0, 20);
var ydata = [1, 4, 5, 9, 10, 14, 15, 15, 11, 10, 5, 5, 4, 8, 7, 5, 5, 5, 8, 10];
var xy = []; // start empty, add each element one at a time
for(var i = 0; i < xdata.length; i++ ) {
xy.push({x: xdata[i], y: ydata[i]});
}
var xscl = d3.scale.linear()
.domain(d3.extent(xy, function(d) {return d.x;})) //use just the x part
.range([margin.left, width + margin.left])
var yscl = d3.scale.linear()
.domain([1, 8]) // use just the y part
.range([height + margin.top, margin.top])
var slice = d3.svg.line()
.x(function(d) { return xscl(d.x);}) // apply the x scale to the x data
.y(function(d) { return yscl(d.y);}) // apply the y scale to the y data
var svg = d3.select("body")
.append("svg")
svg.append('rect') // outline for reference
.attr({x: margin.left, y: margin.top,
width: width,
height: height,
id: "xSliceBox",
stroke: 'black',
'stroke-width': 0.5,
fill:'white'});
svg.append("path")
.attr("class", "line")
.attr("d", slice(xy))
.attr("clip-path", "#xSliceBox")
.style("fill", "none")
.style("stroke", "red")
.style("stroke-width", 2);
</script>
</body>
You can't reference the rectangle directly in the clip-path
property, you need to create a <clipPath>
element. Then, inside the <clipPath>
element, you can use a <use>
element to reference the rectangle.
(Yes, it's round-about and more complicated that you would think it should be, but that's how the SVG specs defined it.)
Working from your code:
var svg = d3.select("body")
.append("svg")
var clip = svg.append("defs").append("clipPath")
.attr("id", "clipBox");
svg.append('rect') // outline for reference
.attr({x: margin.left, y: margin.top,
width: width,
height: height,
id: "xSliceBox",
stroke: 'black',
'stroke-width': 0.5,
fill:'white'});
clip.append("use").attr("xlink:href", "#xSliceBox");
svg.append("path")
.attr("class", "line")
.attr("d", slice(xy))
.attr("clip-path", "url(#clipBox)") //CORRECTION
.style("fill", "none")
.style("stroke", "red")
.style("stroke-width", 2);
You could also do this the other way around, defining the rectangle within the clipPath
element and then using a <use>
element to actually draw it to the screen. Either way, you want to only define the rectangle once, so that if you decide to change it you only have to do it in one place and the other will update to match.
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