Creating complex clipping path for image?

让人想犯罪 __ 提交于 2019-12-24 09:19:38

问题


I'm new to fabricjs (and to Javascript development in general). I am "porting" a legacy Flex/Actionscript project and need to enable the user to create a complex clipping path for an image.

My approach in Actionscript was to use the Actionscript Graphics class using BlendMode.ERASE to "erase" from the yellow base rectangle (i.e. give the appearance of erasing) and then using that set of rects to create a bitmap to serve as an alpha channel for the final image (Step 3) created on the fly.

Can anyone suggest how I might accomplish a similar functionality in Fabric? It doesn't seem to support HTML5 Canvas Blend modes and while I see that it supports clipping paths for images, I'm not seeing how I can enable the user to interactively create a clipping path without doing lots of intersection checks to try to derive the points to create a new path on the fly.

Thanks!


Step 1: After the user has drawn a base rectangle, drag-option/alt-key enables them to draw a rectangle (the red line) which will be subtracted from the base rect.

Step 2: The base rect is shown with the subtraction.

Step 3: The base rect is used to clip or mask a section of the base image


Step 1


Step 2


Step 3


回答1:


Will Tower,

There is no easy way to do it. Here are the steps:

  1. Draw 'Yellow' rectangle
  2. Draw 'Red' rectangle
  3. Use clipping library like PolyBool for intersection and xor operations
  4. Convert drawing result into the clipped path of combining rectangles
  5. clip your image

I created some quick fiddle. You have to click on a each button to clip. It won't clip if you will not add 2 rectangles on the canvas. This is very simple example. In order to work properly you have to draw rectangles with mouse (make them dynamic). Also, this logic is not accounting for these variations (you have to work on them as well): For these use cases Clipping Library will return to you 2 set of results, which means different logic should be implemented.

Actual code without jQuery, FabriJs, and PolyBool libraries:

var imgURL = 'http://fabricjs.com/lib/pug.jpg';
var clipYellowRect = null;
var clipRedRect = null;
var pug = null;
var canvas = new fabric.Canvas('c');

// insert image into canvas
var pugImg = new Image();
pugImg.onload = function (img) {    
    pug = new fabric.Image(pugImg, {
        angle: 0,
        width: 500,
        height: 500,
        left: 100,
        top: 50,
        scaleX: 0.5,
        scaleY: 0.5,
        clipName: 'pug',
    });
    canvas.add(pug);
};
pugImg.src = imgURL;

//draw yellow rectangle
$('#btnYellowRect').on('click', function(){
    clipYellowRect = new fabric.Rect({
      originX: 'left',
      originY: 'top',
      left: 120,
      top: 60,
      width: 200,
      height: 200,
      fill: 'rgba(255,255,0,0.5)',
      strokeWidth: 0,
      selectable: false
  });
canvas.add(clipYellowRect);
});

//draw red rectangle
$('#btnRedRect').on('click', function(){
    clipRedRect = new fabric.Rect({
      originX: 'left',
      originY: 'top',
      left: 90,
      top: 120,
      width: 100,
      height: 100,
      strokeWidth: 3,
      fill: 'transparent',
      stroke: 'rgba(255,0,0,1)', /* use transparent for no fill */
      strokeWidth: 0,
      selectable: false
  });

canvas.add(clipRedRect);
});

//clip
$('#btnClip').on('click', function(){
    var yellowRectRegion = getRegion(clipYellowRect);
  var redRectRegion = getRegion(clipRedRect);
  //determine inersection
  var intersectResult = PolyBool.intersect({
    regions: [yellowRectRegion],
    inverted: false
  }, {
    regions: [redRectRegion],
    inverted: false
  });

  //generate clipping path
  var xorResult = PolyBool.xor({
    regions: [yellowRectRegion],
    inverted: false
  }, {
    regions: intersectResult.regions,
    inverted: false
  });
  clipImage(xorResult.regions[0]);
});

//prepare data for clipping library
function getRegion(rect){
return [[rect.left, rect.top],
                [rect.left + rect.width, rect.top],
        [rect.left + rect.width, rect.top + rect.height],
        [rect.left, rect.top + rect.height]]
}


function clipImage(points){
     //actual clipping 
  pug.clipTo = function (ctx) {
      var scaleXTo1 = (1 / pug.scaleX);
      var scaleYTo1 = (1 / pug.scaleY);
      ctx.save();

      var ctxLeft = -( pug.width / 2 );
      var ctxTop = -( pug.height / 2 );

      ctx.translate( ctxLeft, ctxTop );
      ctx.scale(scaleXTo1, scaleYTo1);
      ctx.beginPath();
      console.log(points)
      ctx.moveTo(points[0][0] - pug.oCoords.tl.x, points[0][1] - pug.oCoords.tl.y);
      for (var i=1; i < points.length; i++){
      ctx.lineTo(points[i][0] - pug.oCoords.tl.x, points[i][1] - pug.oCoords.tl.y);
      }
      ctx.closePath();
      ctx.restore();
      };
      clipYellowRect.remove();
        clipRedRect.remove();
      canvas.renderAll();
}

Hopefully it will help you.



来源:https://stackoverflow.com/questions/41752110/creating-complex-clipping-path-for-image

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