Simple way to use existing object as a clipping pa

2020-02-14 20:04发布

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>

1条回答
别忘想泡老子
2楼-- · 2020-02-14 20:46

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.

查看更多
登录 后发表回答