web-dev-qa-db-fra.com

Objet masque Fabricjs avec transformation

J'essaie de masquer un objet en utilisant Fabric.js pinceau de dessin libre. Cela fonctionne bien si l'objet est dans sa position par défaut et sans aucune transformation. Mais une fois que j'ajoute des transformations à l'objet, le masque est placé dans la mauvaise position. Je ne sais pas comment résoudre ce problème. Quelqu'un peut-il jeter un coup d'oeil?

Je veux pouvoir appliquer toutes les transformations, avant ou après le masque, sans gâcher le masque.

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";
14
lolol

En gros, chaque fois que vous définissez un angle, votre matrice de contexte a été transformée. Pour pouvoir masquer correctement, vous devez revenir à l'état initial des matrices de transformation. Fabricjs gère la première matrice avec le centre d'un objet (calcule le centre d'un objet avec ou sans angle). La deuxième matrice est une matrice en rotation et la troisième - mise à l'échelle . Pour afficher une image avec toutes les options associées à un objet, vous devez multiplier toutes les matrices:

(First Matrix * Second Matrix) * Third Matrix

Ainsi, l’idée de découpage sera l’ingénierie inverse du contexte en rotation et la multiplication de matrices: différence entre les points centraux d’un objet régulier sans rotation et le point central d’un même objet mais avec rotation. Après cela, prenez le résultat des soustractions et divisez-le par la valeur d'échelle de l'objet d'origine.

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>

1
Observer

J'implémente un exemple avec quelques transformations (scaleX, scaleY, left, top) . J'ai du mal à trouver une solution lorsque l'objet inital a un angle différent de 0. Pour la solution actuelle, j'ai besoin de diviser le mask avec l’échelle object et ajustez également les 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>

Vous pouvez vérifier ici pour le support de loadFromJSON . Le seul problème qui reste est lorsque l’objet est pivoté.

4
Marius Turcu