krx
krx

Reputation: 2683

Keeping a portion of an image always visible on the canvas

I want to make sure that my instance of fabric.Image is always visible.

It doesn't need to be 100% visible, but a portion of it should always be seen. I've seen a few other questions that are similar and I've based my solution on them, but I haven't found anything that completely solves the problem.

My current solution works when no rotation (angles) have been applied to the image. If the angle is at 0 then this solution is perfect, but once the angle start changing I'm having problems.

this.canvas.on('object:moving', function (e) {
    var obj = e.target;
    obj.setCoords();

    var boundingRect = obj.getBoundingRect();
    var max_pad_left_over_width = 50;//obj.currentWidth * .08;
    var max_pad_left_over_height = 50;//obj.currentHeight * .08;
    var max_top_pos = -(obj.currentHeight - max_pad_left_over_height);
    var max_bottom_pos = obj.canvas.height - max_pad_left_over_height;
    var max_left_pos = -(obj.currentWidth - max_pad_left_over_width);
    var max_right_pos = obj.canvas.width - max_pad_left_over_width;

    if(boundingRect.top < max_top_pos) {
        obj.setTop(max_top_pos);
    }

    if(boundingRect.left < max_left_pos){
        obj.setLeft(max_left_pos);
    }

    if(boundingRect.top > max_bottom_pos) {
        obj.setTop(max_bottom_pos);
    }

    if(boundingRect.left > max_right_pos) {
        obj.setLeft(max_right_pos);
    }
});

I've created an example: https://jsfiddle.net/krio/wg0aL8ef/24/

Notice how when no rotation (angle) is applied you cannot force the image to leave the visible canvas. Now, add some rotation to the image and move it around. How can I get the rotated image to also always stay within view? Thanks.

Upvotes: 2

Views: 386

Answers (1)

Swapnil Jain
Swapnil Jain

Reputation: 1516

Fabric.js provides with two object properties isContainedWithinRect and intersectsWithRect which we can use to solve the problem.

An object should not be allowed to go outside the canvas boundaries. This can be achieved if we somehow manage to make sure that the object is either inside the canvas, or at least intersects with the canvas boundaries.

For this case, since you want some portion of the image inside the canvas, we will take a rect smaller than the canvas instead of canvas boundaries to be the containing rect of the image.

Using the properties mentioned to make sure that the above two conditions are satisfied after any movement. Here's how the code looks like:

var canvas = new fabric.Canvas('c');

var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {
    top: 0,
    left: 0

});
imgInstance.setScaleX(0.6);
imgInstance.setScaleY(0.6);
canvas.add(imgInstance);

var prevLeft = 0,
    prevTop = 0;

var max_pad_left_over_width = imgInstance.currentWidth * .08;
var max_pad_left_over_height = imgInstance.currentHeight * .08;
var max_top_pos = max_pad_left_over_height;
var max_bottom_pos = imgInstance.canvas.height - max_pad_left_over_height;
var max_left_pos = max_pad_left_over_width;
var max_right_pos = imgInstance.canvas.width - max_pad_left_over_width;

var pointTL = new fabric.Point(max_left_pos, max_top_pos);
var pointBR = new fabric.Point(max_right_pos, max_bottom_pos);

canvas.on('object:moving', function(e) {
    var obj = e.target;
    obj.setCoords();

    if (!obj.intersectsWithRect(pointTL, pointBR) && !obj.isContainedWithinRect(pointTL, pointBR)) {
        obj.setTop(prevTop);
        obj.setLeft(prevLeft);
    }

    prevLeft = obj.left;
    prevTop = obj.top;
});

Here's the link to the fiddle: https://jsfiddle.net/1ghvjxbk/ and documentation where these properties are mentioned.

Upvotes: 1

Related Questions