Объект маски Fabricjs с преобразованием

Я пытаюсь замаскировать объект, используя Fabric.js рисования Fabric.js. Он отлично работает, если объект находится в позиции по умолчанию и без каких-либо преобразований. Но как только я добавляю преобразования к объекту, маска помещается в неправильное положение. Я не знаю, как это решить. Может кто-нибудь взглянуть?

Я хочу иметь возможность применить любые преобразования до или после маски, не испортив маску.

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,
        pathOffset: {
            x: 0,
            y: 0
        }
    });

    let originalObjLeft = object.left,
        originalObjTop = object.top;

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

    canvas.requestRenderAll();
}

// image

let image = new Image();
let object;

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

    canvas.add(object);
};

image.src = "http://i.imgur.com/8rmMZI3.jpg";

Ответ 1

В основном, когда вы устанавливаете угол, ваша матрица контекста была преобразована. Чтобы правильно маскировать, вам нужно вернуться в исходное состояние матриц преобразования. Fabricjs обрабатывает первую матрицу с центральной точкой объекта (вычисляет центр объекта с или без угла). Вторая матрица - вращающаяся матрица, а третья - масштабирование. Чтобы отобразить изображение со всеми параметрами, заданными для объекта, вам необходимо умножить все матрицы:

(First Matrix * Second Matrix) * Third Matrix

Таким образом, идея отсечения будет представлять собой обратную разработку вращающегося контекста и умножения матриц: разность между центральными точками регулярного объекта без вращения и центральной точкой того же объекта, но с поворотом. После этого возьмите результат вычитания и разделите его на исходное значение масштаба объекта.

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>

Ответ 2

Я реализую пример с некоторыми преобразованиями (scaleX, scaleY, left, top). Мне трудно найти решение, когда исходный объект имеет угол, отличный от 0. Для текущего решения мне нужно разделить масштаб mask масштаб object а также отрегулировать положения.

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>