在 Fabric.js 中处理可加载蒙版的正确方法

Proper way to approach loadable masks in Fabric.js

当我试图用 Fabric.js.

掩盖对象时,我打开了这个赏金

我正在开发的工具应该允许用户在图像对象上绘制遮罩,并在遮罩之前或之后对该对象应用变换(倾斜比例旋转等)。我快要获得这个结果了,但有角度的物体仍然无法正常工作。

我也在尝试使用 toJSONloadFromJSON 将这个对象保存到数据库中,但是在尝试完成几天后我意识到这个解决方案将不起作用,因为任何引用从 JSON 加载时无法访问 ctx 范围之外的内容,因此它们会抛出错误。

    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);
    }

Fabric.js 是这个问题的正确解决方案吗?我应该使用其他东西吗?如果这可以用 Fabric.js 完成,正确的方法是什么?

我用一些自定义属性扩展了 fabric.Image。 我还把面具贴在 fabric.Image 上。 对于 fabric.Image.fromObject 图像加载后,我还需要它加载遮罩(我知道这是一条路径)并附加到图像。 这是一个快速的实现。我很确定这段代码可以简化。 请告诉我是否有不清楚的地方

 

    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
     }
   });
   object = canvas.getObjects()[0];
   object.originalObjLeft = object.left,
     object.originalObjTop = object.top,
     object.originalMaskScaleX = mask.scaleX,
     object.originalMaskScaleY = mask.scaleY,
     object.originalObjScaleX = object.scaleX,
     object.originalObjScaleY = object.scaleY;
     var transformedTranslate = object.translateToGivenOrigin({
        x: object.left,
        y: object.top
    }, object.originX, object.originY, 'center', 'center');
    object.originalTransformLeft = transformedTranslate.x - object.getCenterPoint().x;
    object.originalTransformTop = transformedTranslate.y - object.getCenterPoint().y;
    object.originalAngle = object.angle;
    
    
   object.clipMask = mask;
   object.set({
     clipTo: function(ctx) {
    
        ctx.save();
        ctx.rotate(-this.originalAngle * Math.PI / 180);

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

       
       
       this.clipMask.set({
         left: -object.width / 2 - (this.clipMask.width / 2 * this.originalMaskScaleX) - this.originalObjLeft / this.originalObjScaleX,
         top: -object.height / 2 - (this.clipMask.height / 2 * this.originalMaskScaleY) - this.originalObjTop / this.originalObjScaleY,
         objectCaching: false
       });
       this.clipMask.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: 45,
     top: 50,
     left: 100
   });

   canvas.add(object);
 };

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

 fabric.util.object.extend(fabric.Image.prototype, {
   clipMask: null,
   originalObjLeft: 0,
   originalObjTop: 0,
   originalMaskScaleX: 1,
   originalMaskScaleY: 1,
   originalObjScaleX: 1,
   originalObjScaleY: 1,
   originalAngle:0,
   originalTransformLeft:0,
   originalTransformTop:0
 });
 fabric.Image.prototype.toObject = (function(toObject) {
   return function(propertiesToInclude) {
     return fabric.util.object.extend(toObject.call(this, propertiesToInclude), {
       clipMask: this.clipMask ? this.clipMask.toObject(propertiesToInclude) : null,
       originalObjLeft: this.originalObjLeft,
       originalObjTop: this.originalObjTop,
       originalMaskScaleX: this.originalMaskScaleX,
       originalMaskScaleY: this.originalMaskScaleY,
       originalObjScaleX: this.originalObjScaleX,
       originalObjScaleY: this.originalObjScaleY,
       originalAngle:this.originalAngle,
       originalTransformLeft:this.originalTransformLeft,
       originalTransformTop:this.originalTransformTop
     });
   }
 })(fabric.Image.prototype.toObject);

 fabric.Image.fromObject = (function(fromObject) {
   return function(_object, callback) {
     fromObject.call(this, _object, (function(callback, _object) {
       return function(image) {
         if (image.clipMask) {
           fabric.Path.fromObject(image.clipMask, (function(callback) {
             return function(path) {
               path.pathOffset.x = 0;
               path.pathOffset.y = 0;
               image.clipMask = path;
               callback(image);
             }
           })(callback))
         } else {
           callback(image);
         }
       }
     })(callback, _object));
     return;
   }
 })(fabric.Image.fromObject)




 $("#button1").on('click', function() {
   let dataJSON = canvas.toJSON();
   canvas.clear();
   canvas.loadFromJSON(
     dataJSON,
     canvas.renderAll.bind(canvas));
 })
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<button id="button1">SAve/Load JSON</button>
<div class="canvas__wrapper">
  <canvas id="canvas" width="1280" height="720"></canvas>
</div>

更新 我更新了代码以解决 :

角度的问题