Clip SVG image with rectangle boundaries when it is resized in D3

ps0604

In this jsFiddle I combine D3 with interact.js, both working on SVG. There's a group that contains a rect and an image. The group class is resizable and that works fine. The problem is that the rect, when resized, should clip the image (i.e. the image should never be out of the rectangle borders) but it does not. I use a D3 clipPath for that, but it's not working. What is the problem?

var svg = d3.select("body")
            .append("svg")
            .attr("width", 500)
            .attr("height", 200);

var g = svg.append('g')
           .attr('class', 'resize-me');

var rect = g.append('rect')
    .attr('stroke', 'blue')
    .attr('x', 0)
    .attr('y', 0)
    .attr("width", 200)
    .attr("height", 200)
    .attr('stroke-width', 2)
    .attr('stroke', 'white')
    .attr('fill', 'grey');

var image = g.append('image')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', 128)
      .attr('height', 128)
      .attr("xlink:href", imageUrl);


interact('.resize-me')
    .resizable({
        edges: { left: true, right: true, bottom: true, top: true }
    })
    .on('resizemove', function(event) {

        var target = event.target;
        var rect = target.childNodes[0];
        var img = target.childNodes[1];

        var x = (parseFloat(target.getAttribute('endx')) || 0)
        var y = (parseFloat(target.getAttribute('endy')) || 0)

        rect.setAttribute('width', event.rect.width);
        rect.setAttribute('height', event.rect.height);

        x += event.deltaRect.left
        y += event.deltaRect.top
        rect.setAttribute('transform', 'translate(' + x + ', ' + y + ')')

        rect.setAttribute('endx', x)
        rect.setAttribute('endy', y)


        // clip image

        svg.append('defs')
           .append('clipPath')
           .attr('id', 'clip2')
           .append('rect')
           .attr('x', 0)
           .attr('y', 0)
           .attr('width', event.rect.width)
           .attr('height', event.rect.height);

        image.attr("clip-path", "url(#clip2)");

    });
Alex L

Do you want the image to always expand to fill the grey box? https://jsfiddle.net/alexander_L/u607w315/12/ (version 12 for image expanding)

enter image description here

Also, you are appending <def/> tags every time the event fires:

enter image description here

It gets quickly out of hand.

You should either attach the <def/> to some dummy one element array data and use the d3.js update pattern or simpler you could just create the <def/> tag in your source code and update the attribute of that same tag each time.

You can do it once at the start:

var def = svg.append('defs')
           .append('clipPath')
           .attr('id', 'clip2')
           .append('rect')
           .attr('x', 0)
           .attr('y', 0)
           .attr('width', 200)
           .attr('height', 200);

And then use this variable and update during your event:

def.attr('x', 0)
           .attr('y', 0)
           .attr('width', event.rect.width)
           .attr('height', event.rect.height);

Then you avoid this issue.

https://jsfiddle.net/alexander_L/u607w315/11/ (version 11 for image clipping)

Do you want this behaviour:

enter image description here

The image is clipped when the grey box is smaller in one dimension than the image?

UPDATE

Since the OP noticed a bug in the original code which causes the grey box to always snap back to at least the height or width of the image, I tried to also solve this problem.

However, I also noticed some odd behaviour, that the top left corner of the box could not be extended further up or left so I fixed that first: https://jsfiddle.net/alexander_L/u607w315/25/

See the .gif of the new behaviour and the old bug the OP mentioned:

enter image description here

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related