Fabricjs mask object with transformation

纵饮孤独 提交于 2019-12-04 23:37:53

Basically whenever you set an angle, your context matrix has been transformed. In order to mask properly you need to return to initial state of the Transformation Matrices. Fabricjs handles first matrix with center point of an object (calculates center of an object with or without an angle). Second matrix is rotating matrix, and third - scaling. To display image with all options which are set to an object, you need to multiply all Matrices:

(First Matrix * Second Matrix) * Third Matrix

So the idea of clipping will be reverse engineering of rotating context and multiplications of matrices: difference between center points of regular object without rotation and center point of the same object but with rotation. After that take result of subtractions and divide by original object scale value.

let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});

const angle = 45;
let objectHasBeenRotated = false;

canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;

canvas.on("path:created", function (options) {
clip(options.path);
});

function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);

let mask = new fabric.Path(path.path, {
    top: 0,
    left: 0,
    objectCaching: false,
    strokeWidth: 0,
    scaleX: 1 / object.scaleX,
    scaleY: 1 / object.scaleY,
    pathOffset: {
        x: 0,
        y: 0,
    }
});

let originalObjLeft = object.left,
    originalObjTop = object.top,
    originalMaskScaleX = mask.scaleX,
    originalMaskScaleY = mask.scaleY,
    originalObjScaleX = object.scaleX,
    originalObjScaleY = object.scaleY,
    transformedTranslate = object.translateToGivenOrigin({
        x: object.left,
        y: object.top
    }, object.originX, object.originY, 'center', 'center'),
    originalTransformLeft = transformedTranslate.x - object.getCenterPoint().x,
    originalTransformTop = transformedTranslate.y - object.getCenterPoint().y;
object.set({
    clipTo: function (ctx) {

        ctx.save();
        ctx.rotate(-angle * Math.PI / 180);

        ctx.translate(originalTransformLeft / originalObjScaleX, originalTransformTop / originalObjScaleY)

        mask.set({
            left: -object.width / 2 - (mask.width / 2 * originalMaskScaleX) - originalObjLeft / originalObjScaleX,
            top: -object.height / 2 - (mask.height / 2 * originalMaskScaleY) - originalObjTop / originalObjScaleY,

            objectCaching: false
        });
        mask.render(ctx);
        ctx.restore();
    }
});

canvas.requestRenderAll();
}


// image

let image = new Image();


image.onload = function () {
object = new fabric.Image(image, {
    width: 500,
    height: 500,
    scaleX: 0.8,
    scaleY: 0.8,
    angle: angle,
    top: 50,
    left: 300,
    id: 'pug'
});

canvas.add(object);

};

image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
  <canvas id="canvas" width="1280" height="720"></canvas>
</div>

I implement an exemple with some transformations (scaleX,scaleY,left,top). I'm strugle to find a solution when the inital object have an angle different than 0. For the current solution I need it to divide the maskscale with the object scale and also adjust the positions.

let canvas = new fabric.Canvas("canvas", {
    backgroundColor: "lightgray",
    width: 1280,
    height: 720,
    preserveObjectStacking: true,
    selection: false,
    stateful: true
});

canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;

canvas.on("path:created", function(options) {
    clip(options.path);
});

function clip(path) {
    canvas.isDrawingMode = false;
    canvas.remove(path);

    let mask = new fabric.Path(path.path, {
        top: object.top,
        left: object.left,
        objectCaching: false,
        strokeWidth: 0,
    		scaleX : 1/object.scaleX,
        	scaleY : 1/object.scaleY,
        pathOffset: {
            x: 0,
            y: 0
        }
    });

    let originalObjLeft = object.left,
        originalObjTop = object.top,
        originalMaskScaleX = mask.scaleX,
         originalMaskScaleY = mask.scaleY,
          originalObjScaleX = object.scaleX,
         originalObjScaleY = object.scaleY;

    object.set({
        clipTo: function(ctx) {
       		
            mask.set({
                left: -object.width / 2   -( mask.width / 2  * originalMaskScaleX) - originalObjLeft/originalObjScaleX ,
                top: -object.height / 2   -( mask.height / 2 * originalMaskScaleY) - originalObjTop/originalObjScaleY ,
                objectCaching: false
            });
            mask.render(ctx);
        }
    });

    canvas.requestRenderAll();
}

// image

let image = new Image();
 

image.onload = function() {
    object = new fabric.Image(image, {
        width: 500,
        height: 500,
        scaleX: 0.8,
        scaleY: 0.8,
       // angle: 45,
        top: 50,
        left: 100
    });

    canvas.add(object);
};

image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
  <canvas id="canvas" width="1280" height="720"></canvas>
</div>

You can check here for loadFromJSON support. The only problem remains is when the object is rotated.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!