【问题标题】:Clipping mask using fabricjs使用fabricjs剪贴蒙版
【发布时间】:2016-07-07 19:25:08
【问题描述】:

我目前正在开发使用 FabricJS 进行照片编辑的网络应用程序,我需要实现的功能之一是从 Photoshop 中剪切蒙版。

例如我有this assets:框架、蒙版和图像。我需要在框架内插入图像并用蒙版剪辑它。最棘手的部分是需求:

  1. 用户应该能够修改框架内的图像,例如移动、旋转、倾斜...框架本身也可以在画布内移动。
  2. 层数不受限制,因此用户可以在蒙版图像下方或上方添加对象。
  3. 面具、框架和图像未预定义,用户应该能够上传和使用新资产。

我目前的解决方案是这样的:

  1. 加载资产
  2. 将图像的globalCompositeOperation 设置为source-out
  3. 为图像设置clipTo 函数。
  4. 在画布上以组的形式添加资产

在此解决方案中,clipTo 函数将图像保存在框架的矩形区域内,并在globalCompositeOperation 的帮助下将图像剪切到实际蒙版。乍一看它工作正常,但如果我在这个新添加的组上方添加新层,它将因为globalCompositeOperation="source-out" 规则而被切断。我创建了JSFiddle 来展示这一点。

那么,我还可以试试吗?我在 StackOverflow 上看到了一些关于使用 SVG 进行剪切蒙版的建议,但如果我理解正确,SVG 必须只包含一个路径。由于我的应用程序的第三个要求,这可能是一个问题。

任何正确方向的建议都会有所帮助,因为现在我完全被这个问题所困扰。

【问题讨论】:

    标签: javascript canvas fabricjs


    【解决方案1】:

    您可以通过使用要屏蔽的 Img 对象的 ClipPath 属性来完成此操作。有了这个,您可以屏蔽任何类型的对象。并且您还需要在 Img 对象的 ClipTo 函数中添加一些 Ctx 配置。 检查此链接https://jsfiddle.net/naimsajjad/8w7hye2v/8/

    (function() {
      var img01URL = 'http://fabricjs.com/assets/printio.png';
      var img02URL = 'http://fabricjs.com/lib/pug.jpg';
      var img03URL = 'http://fabricjs.com/assets/ladybug.png';
      var img03URL = 'http://fabricjs.com/assets/ladybug.png';
      var canvas = new fabric.Canvas('c');
      canvas.backgroundColor = "red";
      canvas.setHeight(500);
      canvas.setWidth(500);
    
      canvas.setZoom(1)
      var circle = new fabric.Circle({radius: 40, top: 50, left: 50, fixed: true, fill: '', stroke: '1' });
      canvas.add(circle);
      canvas.renderAll();
    
      fabric.Image.fromURL(img01URL, function(oImg) {
        oImg.scale(.25);
        oImg.left = 10;
        oImg.top = 10;
        oImg.clipPath = circle;
        oImg.clipTo = function(ctx) {
          clipObject(this,ctx)
        }
        canvas.add(oImg);
        canvas.renderAll();
      });
      var bili = new fabric.Path('M85.6,606.2c-13.2,54.5-3.9,95.7,23.3,130.7c27.2,35-3.1,55.2-25.7,66.1C60.7,814,52.2,821,50.6,836.5c-1.6,15.6,19.5,76.3,29.6,86.4c10.1,10.1,32.7,31.9,47.5,54.5c14.8,22.6,34.2,7.8,34.2,7.8c14,10.9,28,0,28,0c24.9,11.7,39.7-4.7,39.7-4.7c12.4-14.8-14-30.3-14-30.3c-16.3-28.8-28.8-5.4-33.5-11.7s-8.6-7-33.5-35.8c-24.9-28.8,39.7-19.5,62.2-24.9c22.6-5.4,65.4-34.2,65.4-34.2c0,34.2,11.7,28.8,28.8,46.7c17.1,17.9,24.9,29.6,47.5,38.9c22.6,9.3,33.5,7.8,53.7,21c20.2,13.2,62.2,10.9,62.2,10.9c18.7,6.2,36.6,0,36.6,0c45.1,0,26.5-15.6,10.1-36.6c-16.3-21-49-3.1-63.8-13.2c-14.8-10.1-51.4-25.7-70-36.6c-18.7-10.9,0-30.3,0-48.2c0-17.9,14-31.9,14-31.9h72.4c0,0,56-3.9,70.8,26.5c14.8,30.3,37.3,36.6,38.1,52.9c0.8,16.3-13.2,17.9-13.2,17.9c-31.1-8.6-31.9,41.2-31.9,41.2c38.1,50.6,112-21,112-21c85.6-7.8,79.4-133.8,79.4-133.8c17.1-12.4,44.4-45.1,62.2-74.7c17.9-29.6,68.5-52.1,113.6-30.3c45.1,21.8,52.9-14.8,52.9-14.8c15.6,2.3,20.2-17.9,20.2-17.9c20.2-22.6-15.6-28-16.3-84c-0.8-56-47.5-66.1-45.1-82.5c2.3-16.3,49.8-68.5,38.1-63.8c-10.2,4.1-53,25.3-63.7,30.7c-0.4-1.4-1.1-3.4-2.5-6.6c-6.2-14-74.7,30.3-74.7,30.3s-108.5,64.2-129.6,68.9c-21,4.7-18.7-9.3-44.3-7c-25.7,2.3-38.5,4.7-154.1-44.4c-115.6-49-326,29.8-326,29.8s-168.1-267.9-28-383.4C265.8,13,78.4-83.3,32.9,168.8C-12.6,420.9,98.9,551.7,85.6,606.2z',{top: 0, left: 180, fixed: true, fill: 'white', stroke: '', scaleX: 0.2, scaleY: 0.2 });
      canvas.add(bili);
      canvas.renderAll();
      fabric.Image.fromURL(img02URL, function(oImg) {
        oImg.scale(0.5);
        oImg.left = 180;
        oImg.top = 0;
        oImg.clipPath = bili;
        oImg.clipTo = function(ctx) {
          clipObject(this,ctx)
        }
        canvas.add(oImg);
        canvas.renderAll();
      });
    
      function clipObject(thisObj,ctx)
      {
        if (thisObj.clipPath) {
          ctx.save();
          if (thisObj.clipPath.fixed) {
            var retina = thisObj.canvas.getRetinaScaling();
            ctx.setTransform(retina, 0, 0, retina, 0, 0);
              // to handle zoom
              ctx.transform.apply(ctx, thisObj.canvas.viewportTransform);
              thisObj.clipPath.transform(ctx);
            }
            
            thisObj.clipPath._render(ctx);
            ctx.restore();
            ctx.clip();
            var x = -thisObj.width / 2, y = -thisObj.height / 2, elementToDraw;
    
            if (thisObj.isMoving === false && thisObj.resizeFilter && thisObj._needsResize()) {
              thisObj._lastScaleX = thisObj.scaleX;
              thisObj._lastScaleY = thisObj.scaleY;
              thisObj.applyResizeFilters();
            }
            elementToDraw = thisObj._element;
            elementToDraw && ctx.drawImage(elementToDraw,
             0, 0, thisObj.width, thisObj.height,
             x, y, thisObj.width, thisObj.height);
            thisObj._stroke(ctx);
            thisObj._renderStroke(ctx);
          }
        }
      })();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"></script>
    <canvas id="c" width="400" height="400"></canvas>

    【讨论】:

      【解决方案2】:

      不确定你想要什么。

      如果您想要加载最后一张图片(名为img2),您发送到后面的图片不会影响上面的图层,请执行以下操作。

      你有maskframeimgimg2

      将它们按以下顺序并使用以下组合设置。

      • img2,源代码
      • img,源代码
      • 掩码,目的地
      • 框架,源代码

      如果你想要别的东西,你必须更详细地解释它。

      就我个人而言,当我向客户提供掩码时,我会授予他们对所有复合方法的完全访问权限,并允许他们弄清楚他们需要做什么才能达到预期的效果。提供一个允许您更改合成设置的 UI,并且图层顺序可以让您更轻松地理清有时令人困惑的画布合成规则。

      【讨论】:

      • 我提供的 JSFiddle 只是一个示例,在我的情况下,画布中的所有元素都是由用户创建的,因此可以有 img3、img4...imgN 甚至是另一组 img、蒙版和框架,所以手动覆盖图层顺序或合成设置不是最佳解决方案。但是,我从来没有考虑过为客户提供更改补偿设置的选项。在我的应用程序中,我有 UI 来更改图层顺序,但我担心添加 comp 设置会变得太混乱。您能否描述一下您如何处理 UI 中图层顺序和合成设置之间的混淆?
      • 不要低估用户学习如何使用好应用的意愿。一个好的 UI 需要毫不费力地获得帮助,它应该不超过一个按键 (F1) 或单击按钮,它应该是上下文感知的(工具提示),并且不引人注目(不会在视觉上或功能上阻止 UI 访问),它从最少的描述开始,但提供了在不中断工作流程的情况下访问信息的额外方法。作为编码员/设计师,您可以通过在创建应用程序之前了解您的应用程序来避免用户混淆。目前的困惑不是我认为的用户。
      【解决方案3】:

      我建议看看这个解决方案。

      Multiple clipping areas on Fabric.js canvas

      您最终会得到一个用于定义蒙版形状的形状图层。然后,该形状将作为 clipTo 应用于您的图像。

      我能想到的一个限制是当你开始旋转各种形状时。我知道我在使用矩形和圆形时效果很好,但是根据我的记忆,多边形遇到了一些问题......然而,这都是在旧版本的 FabricJS 下设置的,所以我可能在那里进行了一些改进没经验。

      我遇到的另一个问题是阴影在传递到运行 FabricJS 的 NodeJS 服务器时没有正确呈现。

      【讨论】:

      • 在我的例子中,掩码以 PNG 格式存储。我可以将它们保存为 SVG 以在 clipTo 中使用,但据我所知,clipTo 函数只有在 SVG 包含一个路径时才能工作。
      • 遗憾的是,我相信您对 SVG 只有一条路径是正确的。 FabricJS 的裁剪区域功能似乎是一个难以使用的领域,在某些方面这是该库的主要限制......
      猜你喜欢
      • 2011-06-23
      • 1970-01-01
      • 1970-01-01
      • 2012-07-07
      • 2011-10-20
      • 2018-08-10
      • 2011-06-27
      • 1970-01-01
      • 2013-08-16
      相关资源
      最近更新 更多